summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim-Philipp Müller <tim@centricular.net>2005-11-30 13:20:57 +0000
committerTim-Philipp Müller <tim@centricular.net>2005-11-30 13:20:57 +0000
commitb1d87b1ffb2ec1f3ffd918cc8023de0011f0220f (patch)
tree4723094170d85b79211f43199fe90f48aff87c1e
parent921168120801c7826f6af386f767047a36cf6adf (diff)
Port pango-based textoverlay, timeoverlay and textrender to 0.9 and add background shading and text wrapping modes. M...
Original commit message from CVS: * configure.ac: * ext/Makefile.am: * ext/pango/Makefile.am: * ext/pango/gstclockoverlay.c: (gst_clock_overlay_base_init), (gst_clock_overlay_render_time), (gst_clock_overlay_get_text), (gst_clock_overlay_class_init), (gst_clock_overlay_init): * ext/pango/gstclockoverlay.h: * ext/pango/gsttextoverlay.c: (gst_text_overlay_base_init), (gst_text_overlay_get_text), (gst_text_overlay_class_init), (gst_text_overlay_finalize), (gst_text_overlay_init), (gst_text_overlay_update_wrap_mode), (gst_text_overlay_setcaps), (gst_text_overlay_text_pad_linked), (gst_text_overlay_text_pad_unlinked), (gst_text_overlay_set_property), (gst_text_overlay_getcaps), (gst_text_overlay_shade_y), (gst_text_overlay_blit_yuv420), (gst_text_overlay_resize_bitmap), (gst_text_overlay_render_text), (gst_text_overlay_push_frame), (gst_text_overlay_pop_video), (gst_text_overlay_pop_text), (gst_text_overlay_collected), (gst_text_overlay_change_state), (plugin_init): * ext/pango/gsttextoverlay.h: * ext/pango/gsttimeoverlay.c: (gst_time_overlay_base_init), (gst_time_overlay_render_time), (gst_time_overlay_get_text), (gst_time_overlay_class_init), (gst_time_overlay_init): * ext/pango/gsttimeoverlay.h: Port pango-based textoverlay, timeoverlay and textrender to 0.9 and add background shading and text wrapping modes. Make timoverlay derive from textoverlay. Also add new clockoverlay element.
-rw-r--r--ChangeLog31
-rw-r--r--configure.ac10
-rw-r--r--ext/Makefile.am9
-rw-r--r--ext/pango/Makefile.am36
-rw-r--r--ext/pango/gstclockoverlay.c119
-rw-r--r--ext/pango/gstclockoverlay.h56
-rw-r--r--ext/pango/gsttextoverlay.c1233
-rw-r--r--ext/pango/gsttextoverlay.h100
-rw-r--r--ext/pango/gsttimeoverlay.c313
-rw-r--r--ext/pango/gsttimeoverlay.h56
10 files changed, 1111 insertions, 852 deletions
diff --git a/ChangeLog b/ChangeLog
index 8f07c166..e94a7116 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2005-11-30 Tim-Philipp Müller <tim at centricular dot net>
+
+ * configure.ac:
+ * ext/Makefile.am:
+ * ext/pango/Makefile.am:
+ * ext/pango/gstclockoverlay.c: (gst_clock_overlay_base_init),
+ (gst_clock_overlay_render_time), (gst_clock_overlay_get_text),
+ (gst_clock_overlay_class_init), (gst_clock_overlay_init):
+ * ext/pango/gstclockoverlay.h:
+ * ext/pango/gsttextoverlay.c: (gst_text_overlay_base_init),
+ (gst_text_overlay_get_text), (gst_text_overlay_class_init),
+ (gst_text_overlay_finalize), (gst_text_overlay_init),
+ (gst_text_overlay_update_wrap_mode), (gst_text_overlay_setcaps),
+ (gst_text_overlay_text_pad_linked),
+ (gst_text_overlay_text_pad_unlinked),
+ (gst_text_overlay_set_property), (gst_text_overlay_getcaps),
+ (gst_text_overlay_shade_y), (gst_text_overlay_blit_yuv420),
+ (gst_text_overlay_resize_bitmap), (gst_text_overlay_render_text),
+ (gst_text_overlay_push_frame), (gst_text_overlay_pop_video),
+ (gst_text_overlay_pop_text), (gst_text_overlay_collected),
+ (gst_text_overlay_change_state), (plugin_init):
+ * ext/pango/gsttextoverlay.h:
+ * ext/pango/gsttimeoverlay.c: (gst_time_overlay_base_init),
+ (gst_time_overlay_render_time), (gst_time_overlay_get_text),
+ (gst_time_overlay_class_init), (gst_time_overlay_init):
+ * ext/pango/gsttimeoverlay.h:
+ Port pango-based textoverlay, timeoverlay and textrender to 0.9
+ and add background shading and text wrapping modes. Make
+ timoverlay derive from textoverlay. Also add new clockoverlay
+ element.
+
2005-11-30 Julien MOUTTE <julien@moutte.net>
* gst/udp/Makefile.am: Moved to netbuffer.
diff --git a/configure.ac b/configure.ac
index 9b6fb920..7d0bb1d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -413,6 +413,15 @@ GST_CHECK_FEATURE(DV1394, [raw1394 and avc1394 library], dv1394src, [
fi
])
+dnl *** pango ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_PANGO, true)
+GST_CHECK_FEATURE(PANGO, [pango], pango, [
+ PKG_CHECK_MODULES(PANGO, pango pangoft2,
+ HAVE_PANGO="yes", HAVE_PANGO="no")
+ AC_SUBST(PANGO_CFLAGS)
+ AC_SUBST(PANGO_LIBS)
+])
+
dnl *** shout2 ***
translit(dnm, m, l) AM_CONDITIONAL(USE_SHOUT2, true)
GST_CHECK_FEATURE(SHOUT2, [shout2 plug-in], shout2send, [
@@ -557,6 +566,7 @@ ext/gconf/Makefile
ext/ladspa/Makefile
ext/libcaca/Makefile
ext/libpng/Makefile
+ext/pango/Makefile
ext/raw1394/Makefile
ext/shout2/Makefile
ext/speex/Makefile
diff --git a/ext/Makefile.am b/ext/Makefile.am
index 3b24c9da..af3020bc 100644
--- a/ext/Makefile.am
+++ b/ext/Makefile.am
@@ -76,11 +76,11 @@ endif
MIKMOD_DIR =
# endif
-# if USE_PANGO
-# PANGO_DIR = pango
-# else
+if USE_PANGO
+PANGO_DIR = pango
+else
PANGO_DIR =
-# endif
+endif
if USE_DV1394
DV1394_DIR = raw1394
@@ -130,6 +130,7 @@ DIST_SUBDIRS = \
ladspa \
libcaca \
libpng \
+ pango \
raw1394 \
shout2 \
speex
diff --git a/ext/pango/Makefile.am b/ext/pango/Makefile.am
index 55bee7b8..5d2fc880 100644
--- a/ext/pango/Makefile.am
+++ b/ext/pango/Makefile.am
@@ -1,16 +1,28 @@
-#PANGO_CFLAGS = -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/freetype2
-#PANGO_LDFLAGS = -Wl,--export-dynamic -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0
-plugin_LTLIBRARIES = libgsttimeoverlay.la libgsttextoverlay.la
+plugin_LTLIBRARIES = libgstpango.la
-noinst_HEADERS = gsttimeoverlay.h gsttextoverlay.h
+noinst_HEADERS = \
+ gstclockoverlay.h \
+ gsttextoverlay.h \
+ gsttextrender.h \
+ gsttimeoverlay.h
-libgsttimeoverlay_la_SOURCES = gsttimeoverlay.c
-libgsttimeoverlay_la_CFLAGS = $(GST_CFLAGS) $(PANGO_CFLAGS) -I$(top_srcdir)/gst/videofilter
-libgsttimeoverlay_la_LIBADD = $(GST_LIBS) $(PANGO_LIBS) -lm
-libgsttimeoverlay_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstpango_la_SOURCES = \
+ gstclockoverlay.c \
+ gsttextoverlay.c \
+ gsttextrender.c \
+ gsttimeoverlay.c
-libgsttextoverlay_la_SOURCES = gsttextoverlay.c
-libgsttextoverlay_la_CFLAGS = $(GST_CFLAGS) $(PANGO_CFLAGS)
-libgsttextoverlay_la_LIBADD = $(GST_LIBS) $(PANGO_LIBS)
-libgsttextoverlay_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstpango_la_CFLAGS = \
+ $(GST_PLUGINS_BASE_CFLAGS) \
+ $(GST_BASE_CFLAGS) \
+ $(GST_CFLAGS) \
+ $(PANGO_CFLAGS)
+
+libgstpango_la_LIBADD = \
+ $(GST_BASE_LIBS) \
+ $(GST_LIBS) \
+ $(PANGO_LIBS)
+
+libgstpango_la_LDFLAGS = \
+ $(GST_PLUGIN_LDFLAGS)
diff --git a/ext/pango/gstclockoverlay.c b/ext/pango/gstclockoverlay.c
new file mode 100644
index 00000000..4e4f7a3c
--- /dev/null
+++ b/ext/pango/gstclockoverlay.c
@@ -0,0 +1,119 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2005> Tim-Philipp Müller <tim@centricular.net>
+ *
+ * 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 <gstclockoverlay.h>
+#include <gst/video/video.h>
+#include <time.h>
+
+static GstElementDetails clock_overlay_details =
+GST_ELEMENT_DETAILS ("Time Overlay",
+ "Filter/Editor/Video",
+ "Overlays the time on a video stream",
+ "Tim-Philipp Müller <tim@centricular.net>");
+
+GST_BOILERPLATE (GstClockOverlay, gst_clock_overlay, GstTextOverlay,
+ GST_TYPE_TEXT_OVERLAY)
+
+ static void gst_clock_overlay_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details (element_class, &clock_overlay_details);
+}
+
+static gchar *
+gst_clock_overlay_render_time (GstClockOverlay * overlay)
+{
+ struct tm t;
+ time_t now;
+
+ now = time (NULL);
+ if (localtime_r (&now, &t) == NULL)
+ return g_strdup ("--:--:--");
+
+ return g_strdup_printf ("%02u:%02u:%02u", t.tm_hour, t.tm_min, t.tm_sec);
+}
+
+static gchar *
+gst_clock_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame)
+{
+ gchar *time_str, *txt, *ret;
+
+ overlay->need_render = TRUE;
+
+ GST_OBJECT_LOCK (overlay);
+ txt = g_strdup (overlay->default_text);
+ GST_OBJECT_UNLOCK (overlay);
+
+ time_str = gst_clock_overlay_render_time (GST_CLOCK_OVERLAY (overlay));
+ if (txt != NULL && *txt != '\0') {
+ ret = g_strdup_printf ("%s %s", txt, time_str);
+ } else {
+ ret = time_str;
+ time_str = NULL;
+ }
+
+ g_free (txt);
+ g_free (time_str);
+
+ return ret;
+}
+
+static void
+gst_clock_overlay_class_init (GstClockOverlayClass * klass)
+{
+ GstTextOverlayClass *gsttextoverlay_class;
+
+ gsttextoverlay_class = (GstTextOverlayClass *) klass;
+
+ gsttextoverlay_class->get_text = gst_clock_overlay_get_text;
+}
+
+static void
+gst_clock_overlay_init (GstClockOverlay * overlay, GstClockOverlayClass * klass)
+{
+ PangoFontDescription *font_description;
+ GstTextOverlay *textoverlay;
+ PangoContext *context;
+
+ textoverlay = GST_TEXT_OVERLAY (overlay);
+
+ context = GST_TEXT_OVERLAY_CLASS (klass)->pango_context;
+
+ pango_context_set_language (context, pango_language_from_string ("en_US"));
+ pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
+
+ font_description = pango_font_description_new ();
+ pango_font_description_set_family (font_description, g_strdup ("Monospace"));
+ pango_font_description_set_style (font_description, PANGO_STYLE_NORMAL);
+ pango_font_description_set_variant (font_description, PANGO_VARIANT_NORMAL);
+ pango_font_description_set_weight (font_description, PANGO_WEIGHT_NORMAL);
+ pango_font_description_set_stretch (font_description, PANGO_STRETCH_NORMAL);
+ pango_font_description_set_size (font_description, 18 * PANGO_SCALE);
+ pango_context_set_font_description (context, font_description);
+ pango_font_description_free (font_description);
+
+ textoverlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
+ textoverlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
+}
diff --git a/ext/pango/gstclockoverlay.h b/ext/pango/gstclockoverlay.h
new file mode 100644
index 00000000..68625076
--- /dev/null
+++ b/ext/pango/gstclockoverlay.h
@@ -0,0 +1,56 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2005> Tim-Philipp Müller <tim@centricular.net>
+ *
+ * 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_CLOCK_OVERLAY_H__
+#define __GST_CLOCK_OVERLAY_H__
+
+#include "gsttextoverlay.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_CLOCK_OVERLAY \
+ (gst_clock_overlay_get_type())
+#define GST_CLOCK_OVERLAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLOCK_OVERLAY,GstClockOverlay))
+#define GST_CLOCK_OVERLAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CLOCK_OVERLAY,GstClockOverlayClass))
+#define GST_IS_CLOCK_OVERLAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLOCK_OVERLAY))
+#define GST_IS_CLOCK_OVERLAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CLOCK_OVERLAY))
+
+typedef struct _GstClockOverlay GstClockOverlay;
+typedef struct _GstClockOverlayClass GstClockOverlayClass;
+
+struct _GstClockOverlay {
+ GstTextOverlay textoverlay;
+};
+
+struct _GstClockOverlayClass {
+ GstTextOverlayClass parent_class;
+};
+
+GType gst_clock_overlay_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_CLOCK_OVERLAY_H__ */
+
diff --git a/ext/pango/gsttextoverlay.c b/ext/pango/gsttextoverlay.c
index fae9c247..ce0329fe 100644
--- a/ext/pango/gsttextoverlay.c
+++ b/ext/pango/gsttextoverlay.c
@@ -21,13 +21,25 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
-#include <gst/gst.h>
+
+#include <gst/video/video.h>
+
#include "gsttextoverlay.h"
+#include "gsttimeoverlay.h"
+#include "gstclockoverlay.h"
+#include "gsttextrender.h"
+
+/* FIXME:
+ * - use proper strides and offset for I420
+ * - if text is wider than the video picture, it does not get
+ * clipped properly during blitting (if wrapping is disabled)
+ * - make 'shading_value' a property (or enum: light/normal/dark/verydark)?
+ */
-GST_DEBUG_CATEGORY_STATIC (pango_debug);
+GST_DEBUG_CATEGORY (pango_debug);
#define GST_CAT_DEFAULT pango_debug
-static GstElementDetails textoverlay_details = {
+static GstElementDetails text_overlay_details = {
"Text Overlay",
"Filter/Editor/Video",
"Adds text strings on top of a video buffer",
@@ -38,30 +50,30 @@ enum
{
ARG_0,
ARG_TEXT,
+ ARG_SHADING,
ARG_VALIGN,
ARG_HALIGN,
- ARG_X0,
- ARG_Y0,
+ ARG_XPAD,
+ ARG_YPAD,
+ ARG_DELTAX,
+ ARG_DELTAY,
+ ARG_WRAP_MODE,
ARG_FONT_DESC
};
-static GstStaticPadTemplate textoverlay_src_template_factory =
+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) I420, "
- "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
);
static GstStaticPadTemplate video_sink_template_factory =
GST_STATIC_PAD_TEMPLATE ("video_sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-raw-yuv, "
- "format = (fourcc) I420, "
- "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
);
static GstStaticPadTemplate text_sink_template_factory =
@@ -71,64 +83,67 @@ static GstStaticPadTemplate text_sink_template_factory =
GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
);
-static void gst_textoverlay_base_init (gpointer g_class);
-static void gst_textoverlay_class_init (GstTextOverlayClass * klass);
-static void gst_textoverlay_init (GstTextOverlay * overlay);
-static void gst_textoverlay_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_textoverlay_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec);
-static GstStateChangeReturn gst_textoverlay_change_state (GstElement * element);
-static void gst_textoverlay_finalize (GObject * object);
-
-
-static GstElementClass *parent_class = NULL;
-
-/*static guint gst_textoverlay_signals[LAST_SIGNAL] = { 0 }; */
-
-
-GType
-gst_textoverlay_get_type (void)
-{
- static GType textoverlay_type = 0;
-
- if (!textoverlay_type) {
- static const GTypeInfo textoverlay_info = {
- sizeof (GstTextOverlayClass),
- gst_textoverlay_base_init,
- NULL,
- (GClassInitFunc) gst_textoverlay_class_init,
- NULL,
- NULL,
- sizeof (GstTextOverlay),
- 0,
- (GInstanceInitFunc) gst_textoverlay_init,
- };
-
- textoverlay_type =
- g_type_register_static (GST_TYPE_ELEMENT, "GstTextOverlay",
- &textoverlay_info, 0);
- }
- return textoverlay_type;
-}
-
-static void
-gst_textoverlay_base_init (gpointer g_class)
+/* These macros are adapted from videotestsrc.c */
+#define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
+#define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
+#define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)
+
+#define I420_Y_OFFSET(w,h) (0)
+#define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
+#define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
+
+#define I420_SIZE(w,h) (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
+
+
+static GstStateChangeReturn gst_text_overlay_change_state (GstElement * element,
+ GstStateChange transition);
+static GstCaps *gst_text_overlay_getcaps (GstPad * pad);
+static gboolean gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps);
+static GstPadLinkReturn gst_text_overlay_text_pad_linked (GstPad * pad,
+ GstPad * peer);
+static void gst_text_overlay_text_pad_unlinked (GstPad * pad);
+static GstFlowReturn gst_text_overlay_collected (GstCollectPads * pads,
+ gpointer data);
+static void gst_text_overlay_finalize (GObject * object);
+static void gst_text_overlay_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+
+
+GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement, GST_TYPE_ELEMENT)
+#define DEFAULT_YPAD 25
+#define DEFAULT_XPAD 25
+#define DEFAULT_DELTAX 0
+#define DEFAULT_DELTAY 0
+/* keep wrap enum in sync with string in class_init */
+#define DEFAULT_WRAP_MODE GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR
+#define DEFAULT_SHADING_VALUE -80
+ static void gst_text_overlay_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 (&textoverlay_src_template_factory));
+ gst_static_pad_template_get (&src_template_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&video_sink_template_factory));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&text_sink_template_factory));
- gst_element_class_set_details (element_class, &textoverlay_details);
+ /* ugh */
+ if (!GST_IS_TIME_OVERLAY_CLASS (g_class) &&
+ !GST_IS_CLOCK_OVERLAY_CLASS (g_class)) {
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&text_sink_template_factory));
+ }
+
+ gst_element_class_set_details (element_class, &text_overlay_details);
+}
+
+static gchar *
+gst_text_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame)
+{
+ return g_strdup (overlay->default_text);
}
static void
-gst_textoverlay_class_init (GstTextOverlayClass * klass)
+gst_text_overlay_class_init (GstTextOverlayClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
@@ -136,18 +151,22 @@ gst_textoverlay_class_init (GstTextOverlayClass * klass)
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
- parent_class = g_type_class_peek_parent (klass);
+ gobject_class->finalize = gst_text_overlay_finalize;
+ gobject_class->set_property = gst_text_overlay_set_property;
- gobject_class->finalize = gst_textoverlay_finalize;
- gobject_class->set_property = gst_textoverlay_set_property;
- gobject_class->get_property = gst_textoverlay_get_property;
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_text_overlay_change_state);
- gstelement_class->change_state = gst_textoverlay_change_state;
+ klass->get_text = gst_text_overlay_get_text;
klass->pango_context = pango_ft2_get_context (72, 72);
+
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TEXT,
g_param_spec_string ("text", "text",
- "Text to be display,"
- " in pango markup format.", "", G_PARAM_WRITABLE));
+ "Text to be display.", "", G_PARAM_WRITABLE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SHADING,
+ g_param_spec_boolean ("shaded-background", "shaded background",
+ "Whether to shade the background under the text area", FALSE,
+ G_PARAM_WRITABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_VALIGN,
g_param_spec_string ("valign", "vertical alignment",
"Vertical alignment of the text. "
@@ -158,16 +177,27 @@ gst_textoverlay_class_init (GstTextOverlayClass * klass)
"Horizontal alignment of the text. "
"Can be either 'left', 'right', or 'center'",
"center", G_PARAM_WRITABLE));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_X0,
- g_param_spec_int ("x0", "X position",
- "Initial X position."
- " Horizontal aligment takes this point"
- " as reference.", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_Y0,
- g_param_spec_int ("y0", "Y position",
- "Initial Y position."
- " Vertical aligment takes this point"
- " as reference.", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_XPAD,
+ g_param_spec_int ("xpad", "horizontal paddding",
+ "Horizontal paddding when using left/right alignment",
+ 0, G_MAXINT, DEFAULT_XPAD, G_PARAM_WRITABLE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_YPAD,
+ g_param_spec_int ("ypad", "vertical padding",
+ "Vertical padding when using top/bottom alignment",
+ 0, G_MAXINT, DEFAULT_YPAD, G_PARAM_WRITABLE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DELTAX,
+ g_param_spec_int ("deltax", "X position modifier",
+ "Shift X position to the left or to the right. Unit is pixels.",
+ G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DELTAY,
+ g_param_spec_int ("deltay", "Y position modifier",
+ "Shift Y position up or down. Unit is pixels.",
+ G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_WRAP_MODE,
+ g_param_spec_string ("wrap-mode", "wrap mode",
+ "Whether to wrap the text and if so how."
+ "Can be either 'none', 'word', 'char' or 'wordchar'",
+ "wordchar", G_PARAM_WRITABLE));
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 "
@@ -177,162 +207,407 @@ gst_textoverlay_class_init (GstTextOverlayClass * klass)
" for syntax.", "", G_PARAM_WRITABLE));
}
+static void
+gst_text_overlay_finalize (GObject * object)
+{
+ GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
+
+ gst_collect_pads_stop (overlay->collect);
+ gst_object_unref (overlay->collect);
+
+ g_free (overlay->default_text);
+ g_free (overlay->bitmap.buffer);
+
+ if (overlay->layout)
+ g_object_unref (overlay->layout);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
static void
-resize_bitmap (GstTextOverlay * overlay, int width, int height)
+gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
{
- FT_Bitmap *bitmap = &overlay->bitmap;
- int pitch = (width | 3) + 1;
- int size = pitch * height;
+ /* video sink */
+ overlay->video_sinkpad =
+ gst_pad_new_from_template (gst_static_pad_template_get
+ (&video_sink_template_factory), "video_sink");
+ gst_pad_set_getcaps_function (overlay->video_sinkpad,
+ GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
+ gst_pad_set_setcaps_function (overlay->video_sinkpad,
+ GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps));
+ gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
- /* 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 (!GST_IS_TIME_OVERLAY_CLASS (klass) && !GST_IS_CLOCK_OVERLAY_CLASS (klass)) {
+ /* text sink */
+ overlay->text_sinkpad =
+ gst_pad_new_from_template (gst_static_pad_template_get
+ (&text_sink_template_factory), "text_sink");
+ gst_pad_set_link_function (overlay->text_sinkpad,
+ GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_linked));
+ gst_pad_set_unlink_function (overlay->text_sinkpad,
+ GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_unlinked));
+ gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
}
- 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;
+
+ /* (video) source */
+ overlay->srcpad =
+ gst_pad_new_from_template (gst_static_pad_template_get
+ (&src_template_factory), "src");
+ gst_pad_set_getcaps_function (overlay->srcpad,
+ GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
+ gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
+
+ overlay->layout =
+ pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context);
+ memset (&overlay->bitmap, 0, sizeof (overlay->bitmap));
+
+ overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
+ overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
+ overlay->xpad = DEFAULT_XPAD;
+ overlay->ypad = DEFAULT_YPAD;
+ overlay->deltax = 0;
+ overlay->deltay = 0;
+
+ overlay->wrap_mode = DEFAULT_WRAP_MODE;
+
+ overlay->want_shading = FALSE;
+ overlay->shading_value = DEFAULT_SHADING_VALUE;
+
+ overlay->default_text = g_strdup ("");
+ overlay->need_render = TRUE;
+
+ overlay->fps_n = 0;
+ overlay->fps_d = 1;
+
+ overlay->collect = gst_collect_pads_new ();
+
+ gst_collect_pads_set_function (overlay->collect,
+ GST_DEBUG_FUNCPTR (gst_text_overlay_collected), overlay);
+
+ overlay->video_collect_data = gst_collect_pads_add_pad (overlay->collect,
+ overlay->video_sinkpad, sizeof (GstCollectData));
+
+ /* text pad will be added when it is linked */
+ overlay->text_collect_data = NULL;
}
static void
-render_text (GstTextOverlay * overlay)
+gst_text_overlay_update_wrap_mode (GstTextOverlay * 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, 0, 0);
- overlay->baseline_y = ink_rect.y;
+ if (overlay->wrap_mode == GST_TEXT_OVERLAY_WRAP_MODE_NONE) {
+ GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
+ pango_layout_set_width (overlay->layout, -1);
+ } else {
+ GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
+ GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
+ pango_layout_set_width (overlay->layout, overlay->width * PANGO_SCALE);
+ pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
+ }
}
-/* static GstPadLinkReturn */
-/* gst_textoverlay_text_sinkconnect (GstPad *pad, GstCaps *caps) */
-/* { */
-/* return GST_PAD_LINK_DONE; */
-/* } */
-static GList *
-gst_textoverlay_linkedpads (GstPad * pad)
+/* FIXME: upstream nego (e.g. when the video window is resized) */
+
+static gboolean
+gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps)
{
- GstPad *otherpad;
GstTextOverlay *overlay;
+ GstStructure *structure;
+ gboolean ret = FALSE;
+ const GValue *fps;
+
+ if (!GST_PAD_IS_SINK (pad))
+ return TRUE;
+
+ g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
- overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
- if (pad == overlay->text_sinkpad)
- return NULL;
- otherpad = (pad == overlay->video_sinkpad) ?
- overlay->srcpad : overlay->video_sinkpad;
+ overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
- return g_list_append (NULL, otherpad);
+ overlay->width = 0;
+ overlay->height = 0;
+ structure = gst_caps_get_structure (caps, 0);
+ fps = gst_structure_get_value (structure, "framerate");
+
+ if (gst_structure_get_int (structure, "width", &overlay->width) &&
+ gst_structure_get_int (structure, "height", &overlay->height) &&
+ fps != NULL) {
+ ret = gst_pad_set_caps (overlay->srcpad, caps);
+ }
+
+ overlay->fps_n = gst_value_get_fraction_numerator (fps);
+ overlay->fps_d = gst_value_get_fraction_denominator (fps);
+
+ if (ret) {
+ GST_OBJECT_LOCK (overlay);
+ gst_text_overlay_update_wrap_mode (overlay);
+ GST_OBJECT_UNLOCK (overlay);
+ }
+
+ return ret;
}
static GstPadLinkReturn
-gst_textoverlay_link (GstPad * pad, const GstCaps * caps)
+gst_text_overlay_text_pad_linked (GstPad * pad, GstPad * peer)
{
- GstPad *otherpad;
GstTextOverlay *overlay;
- GstStructure *structure;
- GstPadLinkReturn ret;
- overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
- otherpad = (pad == overlay->video_sinkpad) ?
- overlay->srcpad : overlay->video_sinkpad;
+ overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
- ret = gst_pad_try_set_caps (otherpad, caps);
- if (GST_PAD_LINK_FAILED (ret))
- return ret;
+ GST_DEBUG_OBJECT (overlay, "Text pad linked");
- 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);
+ if (overlay->text_collect_data == NULL) {
+ overlay->text_collect_data = gst_collect_pads_add_pad (overlay->collect,
+ overlay->text_sinkpad, sizeof (GstCollectData));
+ }
- return ret;
+ overlay->need_render = TRUE;
+
+ return GST_PAD_LINK_OK;
}
-static GstCaps *
-gst_textoverlay_getcaps (GstPad * pad)
+static void
+gst_text_overlay_text_pad_unlinked (GstPad * pad)
{
- GstPad *otherpad;
GstTextOverlay *overlay;
- GstCaps *caps, *rcaps;
- const GstCaps *tcaps;
- overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
- otherpad = (pad == overlay->video_sinkpad) ?
- overlay->srcpad : overlay->video_sinkpad;
+ /* don't use gst_pad_get_parent() here, will deadlock */
+ overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
+
+ GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
- caps = gst_pad_get_allowed_caps (otherpad);
- tcaps = gst_pad_get_pad_template_caps (pad);
- rcaps = gst_caps_intersect (caps, tcaps);
- gst_caps_free (caps);
+ if (overlay->text_collect_data) {
+ gst_collect_pads_remove_pad (overlay->collect, overlay->text_sinkpad);
+ overlay->text_collect_data = NULL;
+ }
- return rcaps;
+ overlay->need_render = TRUE;
}
-static gboolean
-gst_textoverlay_event (GstPad * pad, GstEvent * event)
+static void
+gst_text_overlay_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
+
+ GST_OBJECT_LOCK (overlay);
+
+ switch (prop_id) {
+ case ARG_TEXT:
+ g_free (overlay->default_text);
+ overlay->default_text = g_value_dup_string (value);
+ overlay->need_render = TRUE;
+ break;
+
+ case ARG_SHADING:{
+ overlay->want_shading = g_value_get_boolean (value);
+ break;
+ }
+ case ARG_XPAD:{
+ overlay->xpad = g_value_get_int (value);
+ break;
+ }
+ case ARG_YPAD:{
+ overlay->ypad = g_value_get_int (value);
+ break;
+ }
+ case ARG_DELTAX:{
+ overlay->deltax = g_value_get_int (value);
+ break;
+ }
+ case ARG_DELTAY:{
+ overlay->deltay = g_value_get_int (value);
+ break;
+ }
+
+ case ARG_VALIGN:{
+ const gchar *s = g_value_get_string (value);
+
+ if (g_ascii_strcasecmp (s, "baseline") == 0)
+ overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
+ else if (g_ascii_strcasecmp (s, "bottom") == 0)
+ overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM;
+ else if (g_ascii_strcasecmp (s, "top") == 0)
+ overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
+ else
+ g_warning ("Invalid 'valign' property value: %s", s);
+ break;
+ }
+
+ case ARG_HALIGN:{
+ const gchar *s = g_value_get_string (value);
+
+ if (g_ascii_strcasecmp (s, "left") == 0)
+ overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
+ else if (g_ascii_strcasecmp (s, "right") == 0)
+ overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
+ else if (g_ascii_strcasecmp (s, "center") == 0)
+ overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
+ else
+ g_warning ("Invalid 'halign' property value: %s", s);
+ break;
+ }
+
+ case ARG_WRAP_MODE:{
+ const gchar *s = g_value_get_string (value);
+
+ if (g_ascii_strcasecmp (s, "none") == 0)
+ overlay->wrap_mode = GST_TEXT_OVERLAY_WRAP_MODE_NONE;
+ else if (g_ascii_strcasecmp (s, "char") == 0)
+ overlay->wrap_mode = GST_TEXT_OVERLAY_WRAP_MODE_CHAR;
+ else if (g_ascii_strcasecmp (s, "word") == 0)
+ overlay->wrap_mode = GST_TEXT_OVERLAY_WRAP_MODE_WORD;
+ else if (g_ascii_strcasecmp (s, "wordchar") == 0)
+ overlay->wrap_mode = GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR;
+ else
+ g_warning ("Invalid 'wrap-mode' property value: %s", s);
+
+ gst_text_overlay_update_wrap_mode (overlay);
+ break;
+ }
+
+ case ARG_FONT_DESC:
+ {
+ PangoFontDescription *desc;
+ const gchar *fontdesc_str;
+
+ fontdesc_str = g_value_get_string (value);
+ desc = pango_font_description_from_string (fontdesc_str);
+ if (desc) {
+ GST_LOG ("font description set: %s", fontdesc_str);
+ pango_layout_set_font_description (overlay->layout, desc);
+ pango_font_description_free (desc);
+ } else {
+ GST_WARNING ("font description parse failed: %s", fontdesc_str);
+ }
+ break;
+ }
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ overlay->need_render = TRUE;
+
+ GST_OBJECT_UNLOCK (overlay);
+}
+
+
+static GstCaps *
+gst_text_overlay_getcaps (GstPad * pad)
{
- GstTextOverlay *overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
+ GstTextOverlay *overlay;
+ GstPad *otherpad;
+ GstCaps *caps;
+
+ overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
- if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK &&
- GST_PAD_IS_LINKED (overlay->text_sinkpad)) {
- gst_event_ref (event);
- gst_pad_send_event (GST_PAD_PEER (overlay->text_sinkpad), event);
+ if (pad == overlay->srcpad)
+ otherpad = overlay->video_sinkpad;
+ else
+ otherpad = overlay->srcpad;
+
+ /* we can do what the peer can */
+ caps = gst_pad_peer_get_caps (otherpad);
+ if (caps) {
+ GstCaps *temp;
+ const GstCaps *templ;
+
+ GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
+
+ /* filtered against our padtemplate */
+ templ = gst_pad_get_pad_template_caps (otherpad);
+ GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
+ temp = gst_caps_intersect (caps, templ);
+ GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
+ gst_caps_unref (caps);
+ /* this is what we can do */
+ caps = temp;
+ } else {
+ /* no peer, our padtemplate is enough then */
+ caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
}
- return gst_pad_send_event (GST_PAD_PEER (overlay->video_sinkpad), event);
+ GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
+
+ gst_object_unref (overlay);
+
+ return caps;
}
-static void
+#define BOX_XPAD 6
+#define BOX_YPAD 6
+
+static inline void
+gst_text_overlay_shade_y (GstTextOverlay * overlay, guchar * dest,
+ guint dest_stride, gint x0, gint x1, gint y0, gint y1)
+{
+ gint i, j;
+
+ x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
+ x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
+
+ y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
+ y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
+
+ for (i = y0; i < y1; ++i) {
+ for (j = x0; j < x1; ++j) {
+ gint y = dest[(i * dest_stride) + j] + overlay->shading_value;
+
+ dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
+ }
+ }
+}
+
+/* FIXME:
+ * - use proper strides and offset for I420
+ * - don't draw over the edge of the picture (try a longer
+ * text with a huge font size)
+ */
+
+static inline void
gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
- guchar * pixbuf, int x0, int y0)
+ guint8 * yuv_pixels, gint x0, gint y0)
{
int y; /* text bitmap coordinates */
int x1, y1; /* video buffer coordinates */
- int rowinc, bit_rowinc, uv_rowinc;
- guchar *p, *bitp, *u_p;
- int video_width = overlay->width, video_height = overlay->height;
- int bitmap_x0 = x0 < 1 ? -(x0 - 1) : 1; /* 1 pixel border */
+ int bit_rowinc, uv_rowinc;
+ guint8 *p, *bitp, *u_p;
+ int video_width, video_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 u_plane_size;
int skip_y, skip_x;
- guchar v;
+ guint8 v;
+
+ video_width = I420_Y_ROWSTRIDE (overlay->width);
+ video_height = overlay->height;
+
+/*
+ if (x0 < 0 && abs (x0) < bitmap_width) {
+ bitmap_x0 = abs (x0);
+ x0 = 0;
+ }
+*/
- if (x0 + bitmap_x0 + bitmap_width > video_width - 1) /* 1 pixel border */
- bitmap_width -= x0 + bitmap_x0 + bitmap_width - video_width + 1;
+ if (x0 + bitmap_x0 + bitmap_width > overlay->width - 1) /* 1 pixel border */
+ bitmap_width -= x0 + bitmap_x0 + bitmap_width - overlay->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;
uv_rowinc = video_width / 2 - bitmap_width / 2;
bit_rowinc = bitmap->pitch - bitmap_width;
u_plane_size = (video_width / 2) * (video_height / 2);
y1 = y0 + bitmap_y0;
x1 = x0 + bitmap_x0;
- p = pixbuf + video_width * y1 + x1;
bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
- for (y = bitmap_y0; y < bitmap_height; y++) {
+ for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
int n;
+ p = yuv_pixels + (y + y0) * I420_Y_ROWSTRIDE (overlay->width) + x1;
for (n = bitmap_width; n > 0; --n) {
v = *bitp;
if (v) {
@@ -344,7 +619,6 @@ gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
p++;
bitp++;
}
- p += rowinc;
bitp += bit_rowinc;
}
@@ -352,14 +626,14 @@ gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
y1 = y0 + bitmap_y0;
x1 = x0 + bitmap_x0;
bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
- p = pixbuf + video_width * y1 + x1;
+ p = yuv_pixels + video_width * y1 + x1;
u_p =
- pixbuf + video_width * video_height + (video_width >> 1) * (y1 >> 1) +
+ yuv_pixels + video_width * video_height + (video_width >> 1) * (y1 >> 1) +
(x1 >> 1);
skip_y = 0;
skip_x = 0;
- for (; y < bitmap_height; y++) {
+ for (; y < bitmap_y0 + bitmap_height; y++) {
int n;
x1 = x0 + bitmap_x0;
@@ -381,428 +655,363 @@ gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
bitp++;
}
/*if (!skip_x && !skip_y) u_p--; */
- p += rowinc;
+ p += I420_Y_ROWSTRIDE (overlay->width) - bitmap_width;
bitp += bit_rowinc;
skip_y = !skip_y;
u_p += skip_y ? uv_rowinc : 0;
}
}
-
static void
-gst_textoverlay_video_chain (GstPad * pad, GstData * _data)
+gst_text_overlay_resize_bitmap (GstTextOverlay * overlay, gint width,
+ gint height)
{
- GstBuffer *buf = GST_BUFFER (_data);
- GstTextOverlay *overlay;
- guchar *pixbuf;
- gint x0, y0;
-
- g_return_if_fail (pad != NULL);
- g_return_if_fail (GST_IS_PAD (pad));
- g_return_if_fail (buf != NULL);
- overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
- g_return_if_fail (overlay != NULL);
- g_return_if_fail (GST_IS_TEXTOVERLAY (overlay));
-
- pixbuf = GST_BUFFER_DATA (buf);
+ FT_Bitmap *bitmap = &overlay->bitmap;
+ int pitch = (width | 3) + 1;
+ int size = pitch * height;
- x0 = overlay->x0;
- y0 = overlay->y0;
- switch (overlay->valign) {
- case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
- y0 = overlay->height - overlay->bitmap.rows - y0;
- break;
- case GST_TEXT_OVERLAY_VALIGN_BASELINE: /* ? */
- y0 -= (overlay->bitmap.rows - overlay->baseline_y);
- break;
- case GST_TEXT_OVERLAY_VALIGN_TOP:
- break;
+ /* 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;
}
-
- switch (overlay->halign) {
- case GST_TEXT_OVERLAY_HALIGN_LEFT:
- break;
- case GST_TEXT_OVERLAY_HALIGN_RIGHT:
- x0 = overlay->width - overlay->bitmap.width - x0;
- break;
- case GST_TEXT_OVERLAY_HALIGN_CENTER:
- x0 = (overlay->width - overlay->bitmap.width) / 2;
- break;
+ if (!bitmap->buffer) {
+ /* initialize */
+ bitmap->pixel_mode = ft_pixel_mode_grays;
+ bitmap->num_grays = 256;
}
-
- if (overlay->bitmap.buffer)
- gst_text_overlay_blit_yuv420 (overlay, &overlay->bitmap, pixbuf, x0, y0);
-
- gst_pad_push (overlay->srcpad, GST_DATA (buf));
+ overlay->bitmap_buffer_size = size;
+ bitmap->buffer = g_realloc (bitmap->buffer, size);
+ memset (bitmap->buffer, 0, size);
+ bitmap->rows = height;
+ bitmap->width = width;
+ bitmap->pitch = pitch;
}
-#define GST_DATA_TIMESTAMP(data) \
- (GST_IS_EVENT (data) ? \
- GST_EVENT_TIMESTAMP (GST_EVENT (data)) : \
- GST_BUFFER_TIMESTAMP (GST_BUFFER (data)))
-#define GST_DATA_DURATION(data) \
- (GST_IS_EVENT (data) ? \
- ((GST_EVENT_TYPE (data) == GST_EVENT_FILLER) ? gst_event_filler_get_duration (GST_EVENT (data)) : \
- GST_CLOCK_TIME_NONE) : \
- GST_BUFFER_DURATION (GST_BUFFER (data)))
-
-#define PAST_END(data, time) \
- (GST_DATA_TIMESTAMP (data) != GST_CLOCK_TIME_NONE && \
- GST_DATA_DURATION (data) != GST_CLOCK_TIME_NONE && \
- GST_DATA_TIMESTAMP (data) + GST_DATA_DURATION (data) \
- < (time))
-
static void
-gst_textoverlay_loop (GstElement * element)
+gst_text_overlay_render_text (GstTextOverlay * overlay,
+ const gchar * text, gint textlen)
{
- GstTextOverlay *overlay;
- GstBuffer *video_frame;
- guint64 now;
-
- g_return_if_fail (element != NULL);
- g_return_if_fail (GST_IS_TEXTOVERLAY (element));
- overlay = GST_TEXTOVERLAY (element);
-
- do {
- GST_DEBUG ("Attempting to pull next video frame");
- video_frame = GST_BUFFER (gst_pad_pull (overlay->video_sinkpad));
- if (GST_IS_EVENT (video_frame)) {
- GstEvent *event = GST_EVENT (video_frame);
- GstEventType type = GST_EVENT_TYPE (event);
-
- gst_pad_event_default (overlay->video_sinkpad, event);
- GST_DEBUG ("Received event of type %d", type);
- if (type == GST_EVENT_INTERRUPT)
- return;
- else if (type == GST_EVENT_EOS) {
- /* EOS text stream */
- GstData *data = NULL;
-
- do {
- if (data)
- gst_data_unref (data);
- data = gst_pad_pull (overlay->text_sinkpad);
- } while (!GST_IS_EVENT (data) ||
- GST_EVENT_TYPE (data) == GST_EVENT_EOS);
- gst_data_unref (data);
-
- return;
- }
- video_frame = NULL;
- }
- } while (!video_frame);
- now = GST_BUFFER_TIMESTAMP (video_frame);
- GST_DEBUG ("Got video frame, time=%" GST_TIME_FORMAT, GST_TIME_ARGS (now));
-
- /*
- * This state machine has a bug that can't be resolved easily.
- * (Needs a more complicated state machine.) Basically, if the
- * text that came from a buffer from the sink pad is being
- * displayed, and the default text is changed by set_parameter,
- * we'll incorrectly display the default text.
- *
- * Otherwise, this is a pretty decent state machine that handles
- * buffer timestamps and durations correctly. (I think)
- */
-
- while ((!overlay->current_data ||
- PAST_END (overlay->current_data, now)) &&
- overlay->next_data == NULL) {
- GST_DEBUG ("attempting to pull text data");
-
- /* read all text buffers until we get one "in the future" */
- if (!GST_PAD_IS_USABLE (overlay->text_sinkpad)) {
- break;
- }
- do {
- overlay->next_data = gst_pad_pull (overlay->text_sinkpad);
- if (GST_IS_EVENT (overlay->next_data) &&
- GST_EVENT_TYPE (overlay->next_data) != GST_EVENT_FILLER) {
- GstEvent *event = GST_EVENT (overlay->next_data);
- GstEventType type = GST_EVENT_TYPE (event);
-
- gst_event_unref (event);
- if (type == GST_EVENT_EOS)
- break;
- else if (type == GST_EVENT_INTERRUPT)
- return;
- overlay->next_data = NULL;
- }
- } while (!overlay->next_data);
-
- if (PAST_END (overlay->next_data, now)) {
- GST_DEBUG ("Received %s is past end (%" GST_TIME_FORMAT " + %"
- GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")",
- GST_IS_EVENT (overlay->next_data) ? "event" : "buffer",
- GST_TIME_ARGS (GST_DATA_TIMESTAMP (overlay->next_data)),
- GST_TIME_ARGS (GST_DATA_DURATION (overlay->next_data)),
- GST_TIME_ARGS (now));
- gst_data_unref (overlay->next_data);
- overlay->next_data = NULL;
- } else {
- GST_DEBUG ("Received new text %s of time %" GST_TIME_FORMAT
- "and duration %" GST_TIME_FORMAT,
- GST_IS_EVENT (overlay->next_data) ? "event" : "buffer",
- GST_TIME_ARGS (GST_DATA_TIMESTAMP (overlay->next_data)),
- GST_TIME_ARGS (GST_DATA_DURATION (overlay->next_data)));
- }
- }
-
- if (overlay->next_data &&
- (GST_DATA_TIMESTAMP (overlay->next_data) <= now ||
- GST_DATA_TIMESTAMP (overlay->next_data) == GST_CLOCK_TIME_NONE)) {
- GST_DEBUG ("using new %s",
- GST_IS_EVENT (overlay->next_data) ? "event" : "buffer");
+ PangoRectangle ink_rect, logical_rect;
+ gchar *string;
- if (overlay->current_data) {
- gst_data_unref (overlay->current_data);
- }
- overlay->current_data = overlay->next_data;
- overlay->next_data = NULL;
-
- if (GST_IS_BUFFER (overlay->current_data)) {
- guint size = GST_BUFFER_SIZE (overlay->current_data);
- guint8 *data = GST_BUFFER_DATA (overlay->current_data);
-
- while (size > 0 &&
- (data[size - 1] == '\r' ||
- data[size - 1] == '\n' || data[size - 1] == '\0'))
- size--;
-
- GST_DEBUG ("rendering '%*s'", size,
- GST_BUFFER_DATA (overlay->current_data));
- /* somehow pango barfs over "\0" buffers... */
- pango_layout_set_markup (overlay->layout,
- GST_BUFFER_DATA (overlay->current_data), size);
- } else {
- GST_DEBUG ("Filler - no data");
- pango_layout_set_markup (overlay->layout, "", 0);
- }
- render_text (overlay);
- overlay->need_render = FALSE;
- }
+ if (textlen < 0)
+ textlen = strlen (text);
- if (overlay->current_data && PAST_END (overlay->current_data, now)) {
- GST_DEBUG ("dropping old %s",
- GST_IS_EVENT (overlay->current_data) ? "event" : "buffer");
+ string = g_strndup (text, textlen);
+ g_strdelimit (string, "\n\r\t", ' ');
+ textlen = strlen (string);
- gst_buffer_unref (overlay->current_data);
- overlay->current_data = NULL;
+ /* FIXME: should we check for UTF-8 here? */
- overlay->need_render = TRUE;
+ if (!overlay->need_render) {
+ GST_DEBUG ("Using previously rendered text.");
+ return;
}
- if (overlay->need_render) {
- GST_DEBUG ("rendering '%s'", overlay->default_text);
- pango_layout_set_markup (overlay->layout,
- overlay->default_text, strlen (overlay->default_text));
- render_text (overlay);
+ GST_DEBUG ("Rendering '%s'", string);
+ pango_layout_set_markup (overlay->layout, string, textlen);
- overlay->need_render = FALSE;
- }
+ pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
+ gst_text_overlay_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;
- gst_textoverlay_video_chain (overlay->srcpad, GST_DATA (video_frame));
-}
+ g_free (string);
+ overlay->need_render = FALSE;
+}
-static GstStateChangeReturn
-gst_textoverlay_change_state (GstElement * element, GstStateChange transition)
+static GstFlowReturn
+gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
{
- GstTextOverlay *overlay;
+ gint xpos, ypos;
- overlay = GST_TEXTOVERLAY (element);
+ video_frame = gst_buffer_make_writable (video_frame);
- switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ switch (overlay->halign) {
+ case GST_TEXT_OVERLAY_HALIGN_LEFT:
+ xpos = overlay->xpad;
break;
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ case GST_TEXT_OVERLAY_HALIGN_CENTER:
+ xpos = (overlay->width - overlay->bitmap.width) / 2;
break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
+ case GST_TEXT_OVERLAY_HALIGN_RIGHT:
+ xpos = overlay->width - overlay->bitmap.width - overlay->xpad;
break;
+ default:
+ xpos = 0;
}
+ xpos += overlay->deltax;
- parent_class->change_state (element, transition);
- return GST_STATE_CHANGE_SUCCESS;
+ switch (overlay->valign) {
+ case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
+ ypos = overlay->height - overlay->bitmap.rows - overlay->ypad;
+ break;
+ case GST_TEXT_OVERLAY_VALIGN_BASELINE:
+ ypos = overlay->height - (overlay->bitmap.rows + overlay->ypad);
+ break;
+ case GST_TEXT_OVERLAY_VALIGN_TOP:
+ ypos = overlay->ypad;
+ break;
+ default:
+ ypos = overlay->ypad;
+ break;
+ }
+ ypos += overlay->deltay;
+
+ /* shaded background box */
+ if (overlay->want_shading) {
+ gst_text_overlay_shade_y (overlay,
+ GST_BUFFER_DATA (video_frame),
+ I420_Y_ROWSTRIDE (overlay->width),
+ xpos, xpos + overlay->bitmap.width, ypos, ypos + overlay->bitmap.rows);
+ }
+
+
+ if (overlay->bitmap.buffer) {
+ gst_text_overlay_blit_yuv420 (overlay, &overlay->bitmap,
+ GST_BUFFER_DATA (video_frame), xpos, ypos);
+ }
+
+ return gst_pad_push (overlay->srcpad, video_frame);
}
static void
-gst_textoverlay_finalize (GObject * object)
+gst_text_overlay_pop_video (GstTextOverlay * overlay)
{
- GstTextOverlay *overlay = GST_TEXTOVERLAY (object);
+ GstBuffer *buf;
- if (overlay->default_text) {
- g_free (overlay->default_text);
- overlay->default_text = NULL;
- }
+ buf = gst_collect_pads_pop (overlay->collect, overlay->video_collect_data);
+ g_return_if_fail (buf != NULL);
+ gst_buffer_unref (buf);
+}
- if (overlay->layout) {
- g_object_unref (overlay->layout);
- overlay->layout = NULL;
- }
- if (overlay->bitmap.buffer) {
- g_free (overlay->bitmap.buffer);
- overlay->bitmap.buffer = NULL;
+static void
+gst_text_overlay_pop_text (GstTextOverlay * overlay)
+{
+ GstBuffer *buf;
+
+ if (overlay->text_collect_data) {
+ buf = gst_collect_pads_pop (overlay->collect, overlay->text_collect_data);
+ g_return_if_fail (buf != NULL);
+ gst_buffer_unref (buf);
}
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ overlay->need_render = TRUE;
}
-static void
-gst_textoverlay_init (GstTextOverlay * overlay)
+/* This function is called when there is data on all pads */
+static GstFlowReturn
+gst_text_overlay_collected (GstCollectPads * pads, gpointer data)
{
- /* video sink */
- overlay->video_sinkpad =
- gst_pad_new_from_template (gst_static_pad_template_get
- (&video_sink_template_factory), "video_sink");
- gst_pad_set_link_function (overlay->video_sinkpad, gst_textoverlay_link);
- gst_pad_set_getcaps_function (overlay->video_sinkpad,
- gst_textoverlay_getcaps);
- gst_pad_set_internal_link_function (overlay->video_sinkpad,
- gst_textoverlay_linkedpads);
- gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
+ GstTextOverlayClass *klass;
+ GstTextOverlay *overlay;
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstClockTime now, txt_end, frame_end;
+ GstBuffer *video_frame = NULL;
+ GstBuffer *text_buf = NULL;
+ gchar *text;
+ gint text_len;
+
+ overlay = GST_TEXT_OVERLAY (data);
+ klass = GST_TEXT_OVERLAY_GET_CLASS (data);
+
+ GST_DEBUG ("Collecting");
+
+ video_frame = gst_collect_pads_peek (overlay->collect,
+ overlay->video_collect_data);
+
+ /* send EOS if video stream EOSed regardless of text stream */
+ if (video_frame == NULL) {
+ GST_DEBUG ("Video stream at EOS");
+ if (overlay->text_collect_data) {
+ text_buf = gst_collect_pads_pop (overlay->collect,
+ overlay->text_collect_data);
+ }
+ gst_pad_push_event (overlay->srcpad, gst_event_new_eos ());
+ ret = GST_FLOW_UNEXPECTED;
+ goto done;
+ }
- /* text sink */
- overlay->text_sinkpad =
- gst_pad_new_from_template (gst_static_pad_template_get
- (&text_sink_template_factory), "text_sink");
- gst_pad_set_internal_link_function (overlay->text_sinkpad,
- gst_textoverlay_linkedpads);
- gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
+ if (GST_BUFFER_TIMESTAMP (video_frame) == GST_CLOCK_TIME_NONE) {
+ g_warning ("%s: video frame has invalid timestamp", G_STRLOC);
+ }
- /* (video) source */
- overlay->srcpad =
- gst_pad_new_from_template (gst_static_pad_template_get
- (&textoverlay_src_template_factory), "src");
- gst_pad_set_link_function (overlay->srcpad, gst_textoverlay_link);
- gst_pad_set_getcaps_function (overlay->srcpad, gst_textoverlay_getcaps);
- gst_pad_set_internal_link_function (overlay->srcpad,
- gst_textoverlay_linkedpads);
- gst_pad_set_event_function (overlay->srcpad, gst_textoverlay_event);
- gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
+ now = GST_BUFFER_TIMESTAMP (video_frame);
- overlay->layout =
- pango_layout_new (GST_TEXTOVERLAY_GET_CLASS (overlay)->pango_context);
- memset (&overlay->bitmap, 0, sizeof (overlay->bitmap));
+ if (GST_BUFFER_DURATION (video_frame) != GST_CLOCK_TIME_NONE) {
+ frame_end = now + GST_BUFFER_DURATION (video_frame);
+ } else if (overlay->fps_n > 0) {
+ frame_end = now + gst_util_uint64_scale_int (GST_SECOND,
+ overlay->fps_d, overlay->fps_n);
+ } else {
+ /* magic value, does not really matter since texts
+ * tend to span quite a few frames in practice anyway */
+ frame_end = now + GST_SECOND / 25;
+ }
- overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
- overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
- overlay->x0 = overlay->y0 = 25;
+ GST_DEBUG ("Got video frame: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (now), GST_TIME_ARGS (frame_end));
- overlay->default_text = g_strdup ("");
- overlay->need_render = TRUE;
+ /* text pad not linked? */
+ if (overlay->text_collect_data == NULL) {
+ gchar *txt;
- gst_element_set_loop_function (GST_ELEMENT (overlay), gst_textoverlay_loop);
+ if (klass->get_text)
+ txt = klass->get_text (overlay, video_frame);
+ else
+ txt = g_strdup (overlay->default_text);
- GST_OBJECT_FLAG_SET (overlay, GST_ELEMENT_EVENT_AWARE);
-}
+ GST_DEBUG ("Text pad not linked, rendering default text: '%s'",
+ GST_STR_NULL (txt));
+ if (txt != NULL && *txt != '\0') {
+ gst_text_overlay_render_text (overlay, txt, -1);
+ ret = gst_text_overlay_push_frame (overlay, video_frame);
+ } else {
+ ret = gst_pad_push (overlay->srcpad, video_frame);
+ }
+ gst_text_overlay_pop_video (overlay);
+ video_frame = NULL;
+ goto done;
+ }
+ text_buf = gst_collect_pads_peek (overlay->collect,
+ overlay->text_collect_data);
-static void
-gst_textoverlay_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstTextOverlay *overlay;
+ /* just push the video frame if the text stream has EOSed */
+ if (text_buf == NULL) {
+ GST_DEBUG ("Text pad EOSed, just pushing video frame as is");
+ ret = gst_pad_push (overlay->srcpad, video_frame);
+ gst_text_overlay_pop_video (overlay);
+ video_frame = NULL;
+ goto done;
+ }
- g_return_if_fail (GST_IS_TEXTOVERLAY (object));
- overlay = GST_TEXTOVERLAY (object);
+ /* if the text buffer isn't stamped right, pop it off the
+ * queue and display it for the current video frame only */
+ if (GST_BUFFER_TIMESTAMP (text_buf) == GST_CLOCK_TIME_NONE ||
+ GST_BUFFER_DURATION (text_buf) == GST_CLOCK_TIME_NONE) {
+ GST_WARNING ("Got text buffer with invalid time stamp or duration");
+ gst_text_overlay_pop_text (overlay);
+ GST_BUFFER_TIMESTAMP (text_buf) = now;
+ GST_BUFFER_DURATION (text_buf) = frame_end - now;
+ }
- switch (prop_id) {
+ txt_end = GST_BUFFER_TIMESTAMP (text_buf) + GST_BUFFER_DURATION (text_buf);
- case ARG_TEXT:
- if (overlay->default_text) {
- g_free (overlay->default_text);
- }
- overlay->default_text = g_strdup (g_value_get_string (value));
- overlay->need_render = TRUE;
- break;
+ GST_DEBUG ("Got text buffer: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (text_buf)), GST_TIME_ARGS (txt_end));
- case ARG_VALIGN:
- if (strcasecmp (g_value_get_string (value), "baseline") == 0)
- overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
- else if (strcasecmp (g_value_get_string (value), "bottom") == 0)
- overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM;
- else if (strcasecmp (g_value_get_string (value), "top") == 0)
- overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
- else
- g_warning ("Invalid 'valign' property value: %s",
- g_value_get_string (value));
- break;
+ /* if the text buffer is too old, pop it off the
+ * queue and return so we get a new one next time */
+ if (txt_end < now) {
+ GST_DEBUG ("Text buffer too old, popping off the queue");
+ gst_text_overlay_pop_text (overlay);
+ ret = GST_FLOW_OK;
+ goto done;
+ }
- case ARG_HALIGN:
- if (strcasecmp (g_value_get_string (value), "left") == 0)
- overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
- else if (strcasecmp (g_value_get_string (value), "right") == 0)
- overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
- else if (strcasecmp (g_value_get_string (value), "center") == 0)
- overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
- else
- g_warning ("Invalid 'halign' property value: %s",
- g_value_get_string (value));
- break;
+ /* if the video frame ends before the text even starts,
+ * just push it out as is and pop it off the queue */
+ if (frame_end < GST_BUFFER_TIMESTAMP (text_buf)) {
+ GST_DEBUG ("Video buffer before text, pushing out and popping off queue");
+ ret = gst_pad_push (overlay->srcpad, video_frame);
+ gst_text_overlay_pop_video (overlay);
+ video_frame = NULL;
+ goto done;
+ }
- case ARG_X0:
- overlay->x0 = g_value_get_int (value);
- break;
+ /* text duration overlaps video frame duration */
+ text = g_strndup ((gchar *) GST_BUFFER_DATA (text_buf),
+ GST_BUFFER_SIZE (text_buf));
+ g_strdelimit (text, "\n\r\t", ' ');
+ text_len = strlen (text);
+
+ if (text_len > 0) {
+ GST_DEBUG ("Rendering text '%*s'", text_len, text);;
+ gst_text_overlay_render_text (overlay, text, text_len);
+ } else {
+ GST_DEBUG ("No text to render (empty buffer)");
+ gst_text_overlay_render_text (overlay, " ", 1);
+ }
- case ARG_Y0:
- overlay->y0 = g_value_get_int (value);
- break;
+ g_free (text);
- case ARG_FONT_DESC:
- {
- PangoFontDescription *desc;
+ gst_text_overlay_pop_video (overlay);
+ ret = gst_text_overlay_push_frame (overlay, video_frame);
+ video_frame = NULL;
+ goto done;
- 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;
- }
+done:
+ {
+ if (text_buf)
+ gst_buffer_unref (text_buf);
- default:
- break;
+ if (video_frame)
+ gst_buffer_unref (video_frame);
+
+ return ret;
}
}
-static void
-gst_textoverlay_get_property (GObject * object, guint prop_id, GValue * value,
- GParamSpec * pspec)
+static GstStateChangeReturn
+gst_text_overlay_change_state (GstElement * element, GstStateChange transition)
{
- GstTextOverlay *overlay;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+ GstTextOverlay *overlay = GST_TEXT_OVERLAY (element);
- g_return_if_fail (GST_IS_TEXTOVERLAY (object));
- overlay = GST_TEXTOVERLAY (object);
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ gst_collect_pads_start (overlay->collect);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ /* need to unblock the collectpads before calling the
+ * parent change_state so that streaming can finish */
+ gst_collect_pads_stop (overlay->collect);
+ break;
+ default:
+ break;
+ }
- switch (prop_id) {
+ ret = parent_class->change_state (element, transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ switch (transition) {
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
+
+ return ret;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
- GST_TYPE_TEXTOVERLAY))
+ GST_TYPE_TEXT_OVERLAY) ||
+ !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
+ GST_TYPE_TIME_OVERLAY) ||
+ !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
+ GST_TYPE_CLOCK_OVERLAY) ||
+ !gst_element_register (plugin, "textrender", GST_RANK_NONE,
+ GST_TYPE_TEXT_RENDER)) {
return FALSE;
+ }
/*texttestsrc_plugin_init(module, plugin); */
- /*subparse_plugin_init(module, plugin); */
GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
return TRUE;
}
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "textoverlay",
- "Text overlay", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
- GST_PACKAGE_ORIGIN)
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+ "pango", "Pango-based text rendering and overlay", plugin_init,
+ VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/ext/pango/gsttextoverlay.h b/ext/pango/gsttextoverlay.h
index 669f88d4..65a5b892 100644
--- a/ext/pango/gsttextoverlay.h
+++ b/ext/pango/gsttextoverlay.h
@@ -1,29 +1,30 @@
-/* -*- Mode: C; c-file-style: "stroustrup" -*- */
-#ifndef __GST_TEXTOVERLAY_H__
-#define __GST_TEXTOVERLAY_H__
+#ifndef __GST_TEXT_OVERLAY_H__
+#define __GST_TEXT_OVERLAY_H__
#include <gst/gst.h>
+#include <gst/base/gstcollectpads.h>
#include <pango/pangoft2.h>
G_BEGIN_DECLS
-#define GST_TYPE_TEXTOVERLAY (gst_textoverlay_get_type())
-#define GST_TEXTOVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
- GST_TYPE_TEXTOVERLAY, GstTextOverlay))
-#define GST_TEXTOVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\
- GST_TYPE_ULAW, GstTextOverlay))
-#define GST_TEXTOVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
- GST_TYPE_TEXTOVERLAY, GstTextOverlayClass))
-#define GST_IS_TEXTOVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
- GST_TYPE_TEXTOVERLAY))
-#define GST_IS_TEXTOVERLAY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),\
- GST_TYPE_TEXTOVERLAY))
+#define GST_TYPE_TEXT_OVERLAY (gst_text_overlay_get_type())
+#define GST_TEXT_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
+ GST_TYPE_TEXT_OVERLAY, GstTextOverlay))
+#define GST_TEXT_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\
+ GST_TYPE_TEXT_OVERLAY,GstTextOverlayClass))
+#define GST_TEXT_OVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
+ GST_TYPE_TEXT_OVERLAY, GstTextOverlayClass))
+#define GST_IS_TEXT_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
+ GST_TYPE_TEXT_OVERLAY))
+#define GST_IS_TEXT_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\
+ GST_TYPE_TEXT_OVERLAY))
typedef struct _GstTextOverlay GstTextOverlay;
typedef struct _GstTextOverlayClass GstTextOverlayClass;
-typedef enum _GstTextOverlayVAlign GstTextOverlayVAlign;
-typedef enum _GstTextOverlayHAlign GstTextOverlayHAlign;
+typedef enum _GstTextOverlayVAlign GstTextOverlayVAlign;
+typedef enum _GstTextOverlayHAlign GstTextOverlayHAlign;
+typedef enum _GstTextOverlayWrapMode GstTextOverlayWrapMode;
enum _GstTextOverlayVAlign {
GST_TEXT_OVERLAY_VALIGN_BASELINE,
@@ -37,37 +38,62 @@ enum _GstTextOverlayHAlign {
GST_TEXT_OVERLAY_HALIGN_RIGHT
};
+enum _GstTextOverlayWrapMode {
+ GST_TEXT_OVERLAY_WRAP_MODE_NONE = -1,
+ GST_TEXT_OVERLAY_WRAP_MODE_WORD = PANGO_WRAP_WORD,
+ GST_TEXT_OVERLAY_WRAP_MODE_CHAR = PANGO_WRAP_CHAR,
+ GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR = PANGO_WRAP_WORD_CHAR
+};
+
struct _GstTextOverlay {
- GstElement element;
-
- GstPad *video_sinkpad;
- GstPad *text_sinkpad;
- GstPad *srcpad;
- gint width;
- gint height;
- PangoLayout *layout;
- FT_Bitmap bitmap;
- gint bitmap_buffer_size;
- gint baseline_y;
- GstTextOverlayVAlign valign;
- GstTextOverlayHAlign halign;
- gint x0;
- gint y0;
- GstData *current_data;
- GstData *next_data;
- gchar *default_text;
- gboolean need_render;
+ GstElement element;
+
+ GstPad *video_sinkpad;
+ GstPad *text_sinkpad;
+ GstPad *srcpad;
+
+ GstCollectPads *collect;
+ GstCollectData *video_collect_data;
+ GstCollectData *text_collect_data;
+
+ gint width;
+ gint height;
+ gint fps_n;
+ gint fps_d;
+
+ GstTextOverlayVAlign valign;
+ GstTextOverlayHAlign halign;
+ GstTextOverlayWrapMode wrap_mode;
+
+ gint xpad;
+ gint ypad;
+ gint deltax;
+ gint deltay;
+ gchar *default_text;
+ gboolean want_shading;
+
+ PangoLayout *layout;
+ FT_Bitmap bitmap;
+ gint bitmap_buffer_size;
+ gint baseline_y;
+
+ gboolean need_render;
+
+ gint shading_value; /* for timeoverlay subclass */
};
struct _GstTextOverlayClass {
GstElementClass parent_class;
PangoContext *pango_context;
+
+ gchar * (*get_text) (GstTextOverlay *overlay, GstBuffer *video_frame);
+
};
-GType gst_textoverlay_get_type(void) G_GNUC_CONST;
+GType gst_text_overlay_get_type(void) G_GNUC_CONST;
G_END_DECLS
-#endif /* __GST_TEXTOVERLAY_H */
+#endif /* __GST_TEXT_OVERLAY_H */
diff --git a/ext/pango/gsttimeoverlay.c b/ext/pango/gsttimeoverlay.c
index 7ad0807a..b756172b 100644
--- a/ext/pango/gsttimeoverlay.c
+++ b/ext/pango/gsttimeoverlay.c
@@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- * Copyright (C) <2003> David Schleef <ds@schleef.org>
+ * Copyright (C) <2005> Tim-Philipp Müller <tim@centricular.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -18,215 +18,99 @@
* Boston, MA 02111-1307, USA.
*/
-/* portions derived from:
- * Pango
- * pangoft2topgm.c: Example program to view a UTF-8 encoding file
- * using Pango to render result.
- *
- * Copyright (C) 1999 Red Hat Software
- * Copyright (C) 2001 Sun Microsystems
- */
-
-/*
- * This file was (probably) generated from gsttimeoverlay.c,
- * gsttimeoverlay.c,v 1.7 2003/11/08 02:48:59 dschleef Exp
- */
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-/*#define DEBUG_ENABLED */
-#include <gsttimeoverlay.h>
-#include <string.h>
-#include <math.h>
-#include <pango/pango.h>
-#include <pango/pangoft2.h>
-
-
-/* GstTimeoverlay signals and args */
-enum
-{
- /* FILL ME */
- LAST_SIGNAL
-};
+#include <gst/video/video.h>
-enum
-{
- ARG_0
- /* FILL ME */
-};
-
-static void gst_timeoverlay_base_init (gpointer g_class);
-static void gst_timeoverlay_class_init (gpointer g_class, gpointer class_data);
-static void gst_timeoverlay_init (GTypeInstance * instance, gpointer g_class);
+#include <gsttimeoverlay.h>
-static void gst_timeoverlay_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void gst_timeoverlay_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
+static GstElementDetails time_overlay_details =
+GST_ELEMENT_DETAILS ("Time Overlay",
+ "Filter/Editor/Video",
+ "Overlays the time on a video stream",
+ "Tim-Philipp Müller <tim@centricular.net>");
-static void gst_timeoverlay_planar411 (GstVideofilter * videofilter, void *dest,
- void *src);
-static void gst_timeoverlay_setup (GstVideofilter * videofilter);
+GST_BOILERPLATE (GstTimeOverlay, gst_time_overlay, GstTextOverlay,
+ GST_TYPE_TEXT_OVERLAY)
-GType
-gst_timeoverlay_get_type (void)
+ static void gst_time_overlay_base_init (gpointer g_class)
{
- static GType timeoverlay_type = 0;
-
- if (!timeoverlay_type) {
- static const GTypeInfo timeoverlay_info = {
- sizeof (GstTimeoverlayClass),
- gst_timeoverlay_base_init,
- NULL,
- gst_timeoverlay_class_init,
- NULL,
- NULL,
- sizeof (GstTimeoverlay),
- 0,
- gst_timeoverlay_init,
- };
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- timeoverlay_type = g_type_register_static (GST_TYPE_VIDEOFILTER,
- "GstTimeoverlay", &timeoverlay_info, 0);
- }
- return timeoverlay_type;
+ gst_element_class_set_details (element_class, &time_overlay_details);
}
-static GstVideofilterFormat gst_timeoverlay_formats[] = {
- {"I420", 12, gst_timeoverlay_planar411,},
-};
-
-
-static void
-gst_timeoverlay_base_init (gpointer g_class)
+static gchar *
+gst_time_overlay_render_time (GstTimeOverlay * overlay, GstClockTime time)
{
- static GstElementDetails timeoverlay_details =
- GST_ELEMENT_DETAILS ("Time Overlay",
- "Filter/Editor/Video",
- "Overlays the time on a video stream",
- "David Schleef <ds@schleef.org>");
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- GstVideofilterClass *videofilter_class = GST_VIDEOFILTER_CLASS (g_class);
- int i;
+ guint hours, mins, secs, msecs;
- gst_element_class_set_details (element_class, &timeoverlay_details);
+ if (!GST_CLOCK_TIME_IS_VALID (time))
+ return g_strdup ("");
- for (i = 0; i < G_N_ELEMENTS (gst_timeoverlay_formats); i++) {
- gst_videofilter_class_add_format (videofilter_class,
- gst_timeoverlay_formats + i);
- }
+ hours = (guint) (time / (GST_SECOND * 60 * 60));
+ mins = (guint) ((time / (GST_SECOND * 60)) % 60);
+ secs = (guint) ((time / GST_SECOND) % 60);
+ msecs = (guint) ((time % GST_SECOND) / (1000 * 1000));
- gst_videofilter_class_add_pad_templates (GST_VIDEOFILTER_CLASS (g_class));
+ return g_strdup_printf ("%u:%02u:%02u.%03u", hours, mins, secs, msecs);
}
-static void
-gst_timeoverlay_class_init (gpointer g_class, gpointer class_data)
+static gchar *
+gst_time_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame)
{
- GObjectClass *gobject_class;
- GstVideofilterClass *videofilter_class;
+ GstClockTime time = GST_BUFFER_TIMESTAMP (video_frame);
+ gchar *time_str, *txt, *ret;
- gobject_class = G_OBJECT_CLASS (g_class);
- videofilter_class = GST_VIDEOFILTER_CLASS (g_class);
+ overlay->need_render = TRUE;
-#if 0
- g_object_class_install_property (gobject_class, ARG_METHOD,
- g_param_spec_enum ("method", "method", "method",
- GST_TYPE_TIMEOVERLAY_METHOD, GST_TIMEOVERLAY_METHOD_1,
- G_PARAM_READWRITE));
-#endif
+ if (!GST_CLOCK_TIME_IS_VALID (time)) {
+ GST_DEBUG ("buffer without valid timestamp");
+ return g_strdup ("");
+ }
- gobject_class->set_property = gst_timeoverlay_set_property;
- gobject_class->get_property = gst_timeoverlay_get_property;
+ GST_DEBUG ("buffer with timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (time));
- videofilter_class->setup = gst_timeoverlay_setup;
-}
+ GST_OBJECT_LOCK (overlay);
+ txt = g_strdup (overlay->default_text);
+ GST_OBJECT_UNLOCK (overlay);
-static void
-gst_timeoverlay_init (GTypeInstance * instance, gpointer g_class)
-{
- GstTimeoverlay *timeoverlay = GST_TIMEOVERLAY (instance);
- GstVideofilter *videofilter;
-
- GST_DEBUG ("gst_timeoverlay_init");
+ time_str = gst_time_overlay_render_time (GST_TIME_OVERLAY (overlay), time);
+ if (txt != NULL && *txt != '\0') {
+ ret = g_strdup_printf ("%s %s", txt, time_str);
+ } else {
+ ret = time_str;
+ time_str = NULL;
+ }
- videofilter = GST_VIDEOFILTER (timeoverlay);
+ g_free (txt);
+ g_free (time_str);
- /* do stuff */
+ return ret;
}
static void
-gst_timeoverlay_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
+gst_time_overlay_class_init (GstTimeOverlayClass * klass)
{
- GstTimeoverlay *src;
+ GstTextOverlayClass *gsttextoverlay_class;
- g_return_if_fail (GST_IS_TIMEOVERLAY (object));
- src = GST_TIMEOVERLAY (object);
+ gsttextoverlay_class = (GstTextOverlayClass *) klass;
- GST_DEBUG ("gst_timeoverlay_set_property");
- switch (prop_id) {
-#if 0
- case ARG_METHOD:
- src->method = g_value_get_enum (value);
- break;
-#endif
- default:
- break;
- }
+ gsttextoverlay_class->get_text = gst_time_overlay_get_text;
}
static void
-gst_timeoverlay_get_property (GObject * object, guint prop_id, GValue * value,
- GParamSpec * pspec)
+gst_time_overlay_init (GstTimeOverlay * overlay, GstTimeOverlayClass * klass)
{
- GstTimeoverlay *src;
-
- g_return_if_fail (GST_IS_TIMEOVERLAY (object));
- src = GST_TIMEOVERLAY (object);
-
- switch (prop_id) {
-#if 0
- case ARG_METHOD:
- g_value_set_enum (value, src->method);
- break;
-#endif
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- if (!gst_library_load ("gstvideofilter"))
- return FALSE;
-
- return gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
- GST_TYPE_TIMEOVERLAY);
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "timeoverlay",
- "Time overlay", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
- GST_PACKAGE_ORIGIN)
-
- static void gst_timeoverlay_setup (GstVideofilter * videofilter)
-{
- GstTimeoverlay *timeoverlay;
PangoFontDescription *font_description;
+ GstTextOverlay *textoverlay;
PangoContext *context;
- g_return_if_fail (GST_IS_TIMEOVERLAY (videofilter));
- timeoverlay = GST_TIMEOVERLAY (videofilter);
-
- /* if any setup needs to be done, do it here */
+ textoverlay = GST_TEXT_OVERLAY (overlay);
- /* what does this affect? */
- context = pango_ft2_get_context (100, 100);
+ context = GST_TEXT_OVERLAY_CLASS (klass)->pango_context;
pango_context_set_language (context, pango_language_from_string ("en_US"));
pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
@@ -237,91 +121,10 @@ GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
pango_font_description_set_variant (font_description, PANGO_VARIANT_NORMAL);
pango_font_description_set_weight (font_description, PANGO_WEIGHT_NORMAL);
pango_font_description_set_stretch (font_description, PANGO_STRETCH_NORMAL);
- pango_font_description_set_size (font_description, 12 * PANGO_SCALE);
-
+ pango_font_description_set_size (font_description, 18 * PANGO_SCALE);
pango_context_set_font_description (context, font_description);
+ pango_font_description_free (font_description);
- timeoverlay->context = context;
- timeoverlay->font_description = font_description;
-
-}
-
-static char *
-gst_timeoverlay_print_smpte_time (guint64 time)
-{
- int hours;
- int minutes;
- int seconds;
- int ms;
- double x;
-
- x = rint ((time + 500000) * 1e-6);
-
- hours = floor (x / (60 * 60 * 1000));
- x -= hours * 60 * 60 * 1000;
- minutes = floor (x / (60 * 1000));
- x -= minutes * 60 * 1000;
- seconds = floor (x / (1000));
- x -= seconds * 1000;
- ms = rint (x);
-
- return g_strdup_printf ("%02d:%02d:%02d.%03d", hours, minutes, seconds, ms);
-}
-
-static void
-gst_timeoverlay_planar411 (GstVideofilter * videofilter, void *dest, void *src)
-{
- GstTimeoverlay *timeoverlay;
- int width;
- int height;
- PangoRectangle logical_rect;
- PangoLayout *layout;
- int b_height, b_width;
- FT_Bitmap bitmap;
- char *string;
- int i;
-
- g_return_if_fail (GST_IS_TIMEOVERLAY (videofilter));
- timeoverlay = GST_TIMEOVERLAY (videofilter);
-
- width = gst_videofilter_get_input_width (videofilter);
- height = gst_videofilter_get_input_height (videofilter);
-
- width = gst_videofilter_get_input_width (videofilter);
- height = gst_videofilter_get_input_height (videofilter);
-
- layout = pango_layout_new (timeoverlay->context);
- string =
- gst_timeoverlay_print_smpte_time (GST_BUFFER_TIMESTAMP (videofilter->
- in_buf));
- pango_layout_set_text (layout, string, strlen (string));
- g_free (string);
-
- pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
- pango_layout_set_width (layout, -1);
-
- pango_layout_get_extents (layout, NULL, &logical_rect);
- b_height = PANGO_PIXELS (logical_rect.height);
- b_width = PANGO_PIXELS (logical_rect.width);
-
- //hheight = 20;
-
- memcpy (dest, src, videofilter->from_buf_size);
-
- for (i = 0; i < b_height; i++) {
- memset (dest + i * width, 0, b_width);
- }
- for (i = 0; i < b_height / 2; i++) {
- memset (dest + width * height + i * (width / 2), 128, b_width / 2);
- memset (dest + width * height + (width / 2) * (height / 2) +
- i * (width / 2), 128, b_width / 2);
- }
- bitmap.rows = b_height;
- bitmap.width = b_width;
- bitmap.pitch = width;
- bitmap.buffer = dest;
- bitmap.num_grays = 256;
- bitmap.pixel_mode = ft_pixel_mode_grays;
-
- pango_ft2_render_layout (&bitmap, layout, 0, 0);
+ textoverlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
+ textoverlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
}
diff --git a/ext/pango/gsttimeoverlay.h b/ext/pango/gsttimeoverlay.h
index 505f8f04..7fe90477 100644
--- a/ext/pango/gsttimeoverlay.h
+++ b/ext/pango/gsttimeoverlay.h
@@ -1,5 +1,6 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2005> Tim-Philipp Müller <tim@centricular.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -18,47 +19,38 @@
*/
-#ifndef __GST_TIMEOVERLAY_H__
-#define __GST_TIMEOVERLAY_H__
-
-
-#include <gst/gst.h>
-#include <pango/pango.h>
-
-#include "gstvideofilter.h"
+#ifndef __GST_TIME_OVERLAY_H__
+#define __GST_TIME_OVERLAY_H__
+#include "gsttextoverlay.h"
G_BEGIN_DECLS
-#define GST_TYPE_TIMEOVERLAY \
- (gst_timeoverlay_get_type())
-#define GST_TIMEOVERLAY(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TIMEOVERLAY,GstTimeoverlay))
-#define GST_TIMEOVERLAY_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TIMEOVERLAY,GstTimeoverlayClass))
-#define GST_IS_TIMEOVERLAY(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TIMEOVERLAY))
-#define GST_IS_TIMEOVERLAY_CLASS(obj) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TIMEOVERLAY))
-
-typedef struct _GstTimeoverlay GstTimeoverlay;
-typedef struct _GstTimeoverlayClass GstTimeoverlayClass;
-
-struct _GstTimeoverlay {
- GstVideofilter videofilter;
-
- PangoFontDescription *font_description;
- PangoContext *context;
-
+#define GST_TYPE_TIME_OVERLAY \
+ (gst_time_overlay_get_type())
+#define GST_TIME_OVERLAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TIME_OVERLAY,GstTimeOverlay))
+#define GST_TIME_OVERLAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TIME_OVERLAY,GstTimeOverlayClass))
+#define GST_IS_TIME_OVERLAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TIME_OVERLAY))
+#define GST_IS_TIME_OVERLAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TIME_OVERLAY))
+
+typedef struct _GstTimeOverlay GstTimeOverlay;
+typedef struct _GstTimeOverlayClass GstTimeOverlayClass;
+
+struct _GstTimeOverlay {
+ GstTextOverlay textoverlay;
};
-struct _GstTimeoverlayClass {
- GstVideofilterClass parent_class;
+struct _GstTimeOverlayClass {
+ GstTextOverlayClass parent_class;
};
-GType gst_timeoverlay_get_type(void);
+GType gst_time_overlay_get_type (void);
G_END_DECLS
-#endif /* __GST_TIMEOVERLAY_H__ */
+#endif /* __GST_TIME_OVERLAY_H__ */