diff options
Diffstat (limited to 'ext/pango')
-rw-r--r-- | ext/pango/Makefile.am | 28 | ||||
-rw-r--r-- | ext/pango/gstclockoverlay.c | 240 | ||||
-rw-r--r-- | ext/pango/gstclockoverlay.h | 62 | ||||
-rw-r--r-- | ext/pango/gsttextoverlay.c | 2352 | ||||
-rw-r--r-- | ext/pango/gsttextoverlay.h | 158 | ||||
-rw-r--r-- | ext/pango/gsttextrender.c | 705 | ||||
-rw-r--r-- | ext/pango/gsttextrender.h | 104 | ||||
-rw-r--r-- | ext/pango/gsttimeoverlay.c | 151 | ||||
-rw-r--r-- | ext/pango/gsttimeoverlay.h | 61 |
9 files changed, 0 insertions, 3861 deletions
diff --git a/ext/pango/Makefile.am b/ext/pango/Makefile.am deleted file mode 100644 index 197ed338..00000000 --- a/ext/pango/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -plugin_LTLIBRARIES = libgstpango.la - -noinst_HEADERS = \ - gstclockoverlay.h \ - gsttextoverlay.h \ - gsttextrender.h \ - gsttimeoverlay.h - -libgstpango_la_SOURCES = \ - gstclockoverlay.c \ - gsttextoverlay.c \ - gsttextrender.c \ - gsttimeoverlay.c - -libgstpango_la_CFLAGS = \ - $(GST_PLUGINS_BASE_CFLAGS) \ - $(GST_BASE_CFLAGS) \ - $(GST_CFLAGS) \ - $(PANGO_CFLAGS) -libgstpango_la_LIBADD = \ - $(GST_PLUGINS_BASE_LIBS) \ - $(top_builddir)/gst-libs/gst/video/libgstvideo-$(GST_MAJORMINOR).la \ - $(GST_BASE_LIBS) \ - $(GST_LIBS) \ - $(PANGO_LIBS) -libgstpango_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstpango_la_LIBTOOLFLAGS = --tag=disable-static - diff --git a/ext/pango/gstclockoverlay.c b/ext/pango/gstclockoverlay.c deleted file mode 100644 index 5db475bc..00000000 --- a/ext/pango/gstclockoverlay.c +++ /dev/null @@ -1,240 +0,0 @@ -/* 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. - */ - -/** - * SECTION:element-clockoverlay - * @see_also: #GstTextOverlay, #GstTimeOverlay - * - * This element overlays the current clock time on top of a video - * stream. You can position the text and configure the font details - * using the properties of the #GstTextOverlay class. By default, the - * time is displayed in the top left corner of the picture, with some - * padding to the left and to the top. - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v videotestsrc ! clockoverlay ! xvimagesink - * ]| Display the current time in the top left corner of the video picture - * |[ - * gst-launch -v videotestsrc ! clockoverlay halign=right valign=bottom text="Edge City" shaded-background=true ! ffmpegcolorspace ! ximagesink - * ]| Another pipeline that displays the current time with some leading - * text in the bottom right corner of the video picture, with the background - * of the text being shaded in order to make it more legible on top of a - * bright video background. - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gstclockoverlay.h> -#include <gst/video/video.h> -#include <time.h> - - -#define DEFAULT_PROP_TIMEFORMAT "%H:%M:%S" - -enum -{ - PROP_0, - PROP_TIMEFORMAT, - PROP_LAST -}; - -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_simple (element_class, "Clock overlay", - "Filter/Editor/Video", - "Overlays the current clock time on a video stream", - "Tim-Philipp Müller <tim@centricular.net>"); -} - - -static void gst_clock_overlay_finalize (GObject * object); -static void gst_clock_overlay_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_clock_overlay_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gchar * -gst_clock_overlay_render_time (GstClockOverlay * overlay) -{ - struct tm *t; - time_t now; - gchar buf[256]; - -#ifdef HAVE_LOCALTIME_R - struct tm dummy; -#endif - - now = time (NULL); - -#ifdef HAVE_LOCALTIME_R - /* Need to call tzset explicitly when calling localtime_r for changes - to the timezone between calls to be visible. */ - tzset (); - t = localtime_r (&now, &dummy); -#else - /* on win32 this apparently returns a per-thread struct which would be fine */ - t = localtime (&now); -#endif - - if (t == NULL) - return g_strdup ("--:--:--"); - - if (strftime (buf, sizeof (buf), overlay->format, t) == 0) - return g_strdup (""); - return g_strdup (buf); -} - -/* Called with lock held */ -static gchar * -gst_clock_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame) -{ - gchar *time_str, *txt, *ret; - - overlay->need_render = TRUE; - - txt = g_strdup (overlay->default_text); - - 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) -{ - GObjectClass *gobject_class; - GstTextOverlayClass *gsttextoverlay_class; - - gobject_class = (GObjectClass *) klass; - gsttextoverlay_class = (GstTextOverlayClass *) klass; - - gobject_class->finalize = gst_clock_overlay_finalize; - gobject_class->set_property = gst_clock_overlay_set_property; - gobject_class->get_property = gst_clock_overlay_get_property; - - gsttextoverlay_class->get_text = gst_clock_overlay_get_text; - - g_object_class_install_property (gobject_class, PROP_TIMEFORMAT, - g_param_spec_string ("time-format", "Date/Time Format", - "Format to use for time and date value, as in strftime.", - DEFAULT_PROP_TIMEFORMAT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - - -static void -gst_clock_overlay_finalize (GObject * object) -{ - GstClockOverlay *overlay = GST_CLOCK_OVERLAY (object); - - g_free (overlay->format); - overlay->format = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - - -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_static (font_description, "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; - - overlay->format = g_strdup (DEFAULT_PROP_TIMEFORMAT); -} - - -static void -gst_clock_overlay_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstClockOverlay *overlay = GST_CLOCK_OVERLAY (object); - - GST_OBJECT_LOCK (overlay); - switch (prop_id) { - case PROP_TIMEFORMAT: - g_free (overlay->format); - overlay->format = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (overlay); -} - - -static void -gst_clock_overlay_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstClockOverlay *overlay = GST_CLOCK_OVERLAY (object); - - GST_OBJECT_LOCK (overlay); - switch (prop_id) { - case PROP_TIMEFORMAT: - g_value_set_string (value, overlay->format); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (overlay); -} diff --git a/ext/pango/gstclockoverlay.h b/ext/pango/gstclockoverlay.h deleted file mode 100644 index 74e32e36..00000000 --- a/ext/pango/gstclockoverlay.h +++ /dev/null @@ -1,62 +0,0 @@ -/* 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; - -/** - * GstClockOverlay: - * - * Opaque clockoverlay data structure. - */ -struct _GstClockOverlay { - GstTextOverlay textoverlay; - gchar *format; /* as in strftime () */ -}; - -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 deleted file mode 100644 index 3e4a0ee7..00000000 --- a/ext/pango/gsttextoverlay.c +++ /dev/null @@ -1,2352 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2003> David Schleef <ds@schleef.org> - * Copyright (C) <2006> Julien Moutte <julien@moutte.net> - * Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com> - * Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net> - * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com> - * - * 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-textoverlay - * @see_also: #GstTextRender, #GstClockOverlay, #GstTimeOverlay, #GstSubParse - * - * This plugin renders text on top of a video stream. This can be either - * static text or text from buffers received on the text sink pad, e.g. - * as produced by the subparse element. If the text sink pad is not linked, - * the text set via the "text" property will be rendered. If the text sink - * pad is linked, text will be rendered as it is received on that pad, - * honouring and matching the buffer timestamps of both input streams. - * - * The text can contain newline characters and text wrapping is enabled by - * default. - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v videotestsrc ! textoverlay text="Room A" valign=top halign=left ! xvimagesink - * ]| Here is a simple pipeline that displays a static text in the top left - * corner of the video picture - * |[ - * gst-launch -v filesrc location=subtitles.srt ! subparse ! txt. videotestsrc ! timeoverlay ! textoverlay name=txt shaded-background=yes ! xvimagesink - * ]| Here is another pipeline that displays subtitles from an .srt subtitle - * file, centered at the bottom of the picture and with a rectangular shading - * around the text in the background: - * <para> - * If you do not have such a subtitle file, create one looking like this - * in a text editor: - * |[ - * 1 - * 00:00:03,000 --> 00:00:05,000 - * Hello? (3-5s) - * - * 2 - * 00:00:08,000 --> 00:00:13,000 - * Yes, this is a subtitle. Don't - * you like it? (8-13s) - * - * 3 - * 00:00:18,826 --> 00:01:02,886 - * Uh? What are you talking about? - * I don't understand (18-62s) - * ]| - * </para> - * </refsect2> - */ - -/* FIXME: alloc segment as part of instance struct */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <gst/video/video.h> - -#include "gsttextoverlay.h" -#include "gsttimeoverlay.h" -#include "gstclockoverlay.h" -#include "gsttextrender.h" -#include <string.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 (pango_debug); -#define GST_CAT_DEFAULT pango_debug - -#define DEFAULT_PROP_TEXT "" -#define DEFAULT_PROP_SHADING FALSE -#define DEFAULT_PROP_VALIGNMENT GST_TEXT_OVERLAY_VALIGN_BASELINE -#define DEFAULT_PROP_HALIGNMENT GST_TEXT_OVERLAY_HALIGN_CENTER -#define DEFAULT_PROP_VALIGN "baseline" -#define DEFAULT_PROP_HALIGN "center" -#define DEFAULT_PROP_XPAD 25 -#define DEFAULT_PROP_YPAD 25 -#define DEFAULT_PROP_DELTAX 0 -#define DEFAULT_PROP_DELTAY 0 -#define DEFAULT_PROP_WRAP_MODE GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR -#define DEFAULT_PROP_FONT_DESC "" -#define DEFAULT_PROP_SILENT FALSE -#define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER -#define DEFAULT_PROP_WAIT_TEXT TRUE -#define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE -#define DEFAULT_PROP_VERTICAL_RENDER FALSE - -/* make a property of me */ -#define DEFAULT_SHADING_VALUE -80 - -#define MINIMUM_OUTLINE_OFFSET 1.0 -#define DEFAULT_SCALE_BASIS 640 - -#define COMP_Y(ret, r, g, b) \ -{ \ - ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \ - ret = CLAMP (ret, 0, 255); \ -} - -#define COMP_U(ret, r, g, b) \ -{ \ - ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \ - ret = CLAMP (ret, 0, 255); \ -} - -#define COMP_V(ret, r, g, b) \ -{ \ - ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \ - ret = CLAMP (ret, 0, 255); \ -} - -#define BLEND(ret, alpha, v0, v1) \ -{ \ - ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \ -} - -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -# define CAIRO_ARGB_A 3 -# define CAIRO_ARGB_R 2 -# define CAIRO_ARGB_G 1 -# define CAIRO_ARGB_B 0 -#else -# define CAIRO_ARGB_A 0 -# define CAIRO_ARGB_R 1 -# define CAIRO_ARGB_G 2 -# define CAIRO_ARGB_B 3 -#endif - -enum -{ - PROP_0, - PROP_TEXT, - PROP_SHADING, - PROP_VALIGN, /* deprecated */ - PROP_HALIGN, /* deprecated */ - PROP_HALIGNMENT, - PROP_VALIGNMENT, - PROP_XPAD, - PROP_YPAD, - PROP_DELTAX, - PROP_DELTAY, - PROP_WRAP_MODE, - PROP_FONT_DESC, - PROP_SILENT, - PROP_LINE_ALIGNMENT, - PROP_WAIT_TEXT, - PROP_AUTO_ADJUST_SIZE, - PROP_VERTICAL_RENDER, - PROP_LAST -}; - -static GstStaticPadTemplate src_template_factory = - GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";" - GST_VIDEO_CAPS_xRGB ";" - GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("UYVY")) - ); - -static GstStaticPadTemplate video_sink_template_factory = - GST_STATIC_PAD_TEMPLATE ("video_sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";" - GST_VIDEO_CAPS_xRGB ";" - GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("UYVY")) - ); - -static GstStaticPadTemplate text_sink_template_factory = - GST_STATIC_PAD_TEMPLATE ("text_sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("text/x-pango-markup; text/plain") - ); - -#define GST_TYPE_TEXT_OVERLAY_VALIGN (gst_text_overlay_valign_get_type()) -static GType -gst_text_overlay_valign_get_type (void) -{ - static GType text_overlay_valign_type = 0; - static const GEnumValue text_overlay_valign[] = { - {GST_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"}, - {GST_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"}, - {GST_TEXT_OVERLAY_VALIGN_TOP, "top", "top"}, - {0, NULL, NULL}, - }; - - if (!text_overlay_valign_type) { - text_overlay_valign_type = - g_enum_register_static ("GstTextOverlayVAlign", text_overlay_valign); - } - return text_overlay_valign_type; -} - -#define GST_TYPE_TEXT_OVERLAY_HALIGN (gst_text_overlay_halign_get_type()) -static GType -gst_text_overlay_halign_get_type (void) -{ - static GType text_overlay_halign_type = 0; - static const GEnumValue text_overlay_halign[] = { - {GST_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"}, - {GST_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"}, - {GST_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"}, - {0, NULL, NULL}, - }; - - if (!text_overlay_halign_type) { - text_overlay_halign_type = - g_enum_register_static ("GstTextOverlayHAlign", text_overlay_halign); - } - return text_overlay_halign_type; -} - - -#define GST_TYPE_TEXT_OVERLAY_WRAP_MODE (gst_text_overlay_wrap_mode_get_type()) -static GType -gst_text_overlay_wrap_mode_get_type (void) -{ - static GType text_overlay_wrap_mode_type = 0; - static const GEnumValue text_overlay_wrap_mode[] = { - {GST_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"}, - {GST_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"}, - {GST_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"}, - {GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"}, - {0, NULL, NULL}, - }; - - if (!text_overlay_wrap_mode_type) { - text_overlay_wrap_mode_type = - g_enum_register_static ("GstTextOverlayWrapMode", - text_overlay_wrap_mode); - } - return text_overlay_wrap_mode_type; -} - -#define GST_TYPE_TEXT_OVERLAY_LINE_ALIGN (gst_text_overlay_line_align_get_type()) -static GType -gst_text_overlay_line_align_get_type (void) -{ - static GType text_overlay_line_align_type = 0; - static const GEnumValue text_overlay_line_align[] = { - {GST_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"}, - {GST_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"}, - {GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"}, - {0, NULL, NULL} - }; - - if (!text_overlay_line_align_type) { - text_overlay_line_align_type = - g_enum_register_static ("GstTextOverlayLineAlign", - text_overlay_line_align); - } - return text_overlay_line_align_type; -} - -#define GST_TEXT_OVERLAY_GET_COND(ov) (((GstTextOverlay *)ov)->cond) -#define GST_TEXT_OVERLAY_WAIT(ov) (g_cond_wait (GST_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov))) -#define GST_TEXT_OVERLAY_SIGNAL(ov) (g_cond_signal (GST_TEXT_OVERLAY_GET_COND (ov))) -#define GST_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_TEXT_OVERLAY_GET_COND (ov))) - -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 gboolean gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps); -static gboolean gst_text_overlay_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_text_overlay_src_query (GstPad * pad, GstQuery * query); - -static gboolean gst_text_overlay_video_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_text_overlay_video_chain (GstPad * pad, - GstBuffer * buffer); -static GstFlowReturn gst_text_overlay_video_bufferalloc (GstPad * pad, - guint64 offset, guint size, GstCaps * caps, GstBuffer ** buffer); - -static gboolean gst_text_overlay_text_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_text_overlay_text_chain (GstPad * pad, - GstBuffer * buffer); -static GstPadLinkReturn gst_text_overlay_text_pad_link (GstPad * pad, - GstPad * peer); -static void gst_text_overlay_text_pad_unlink (GstPad * pad); -static void gst_text_overlay_pop_text (GstTextOverlay * overlay); -static void gst_text_overlay_update_render_mode (GstTextOverlay * overlay); - -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); -static void gst_text_overlay_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * - overlay, PangoFontDescription * desc); - -GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement, - GST_TYPE_ELEMENT); - -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 (&src_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&video_sink_template_factory)); - - /* 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_simple (element_class, "Text overlay", - "Filter/Editor/Video", - "Adds text strings on top of a video buffer", - "David Schleef <ds@schleef.org>, " "Zeeshan Ali <zeeshan.ali@nokia.com>"); -} - -static gchar * -gst_text_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame) -{ - return g_strdup (overlay->default_text); -} - -static void -gst_text_overlay_class_init (GstTextOverlayClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - PangoFontMap *fontmap; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - gobject_class->finalize = gst_text_overlay_finalize; - gobject_class->set_property = gst_text_overlay_set_property; - gobject_class->get_property = gst_text_overlay_get_property; - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_text_overlay_change_state); - - klass->get_text = gst_text_overlay_get_text; - fontmap = pango_cairo_font_map_get_default (); - klass->pango_context = - pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT, - g_param_spec_string ("text", "text", - "Text to be display.", DEFAULT_PROP_TEXT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING, - g_param_spec_boolean ("shaded-background", "shaded background", - "Whether to shade the background under the text area", - DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT, - g_param_spec_enum ("valignment", "vertical alignment", - "Vertical alignment of the text", GST_TYPE_TEXT_OVERLAY_VALIGN, - DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT, - g_param_spec_enum ("halignment", "horizontal alignment", - "Horizontal alignment of the text", GST_TYPE_TEXT_OVERLAY_HALIGN, - DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN, - g_param_spec_string ("valign", "vertical alignment", - "Vertical alignment of the text (deprecated; use valignment)", - DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN, - g_param_spec_string ("halign", "horizontal alignment", - "Horizontal alignment of the text (deprecated; use halignment)", - DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD, - g_param_spec_int ("xpad", "horizontal paddding", - "Horizontal paddding when using left/right alignment", 0, G_MAXINT, - DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD, - g_param_spec_int ("ypad", "vertical padding", - "Vertical padding when using top/bottom alignment", 0, G_MAXINT, - DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_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, DEFAULT_PROP_DELTAX, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY, - g_param_spec_int ("deltay", "Y position modifier", - "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT, - DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE, - g_param_spec_enum ("wrap-mode", "wrap mode", - "Whether to wrap the text and if so how.", - GST_TYPE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC, - g_param_spec_string ("font-desc", "font description", - "Pango font description of font to be used for rendering. " - "See documentation of pango_font_description_from_string " - "for syntax.", DEFAULT_PROP_FONT_DESC, - G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - /** - * GstTextOverlay:line-alignment - * - * Alignment of text lines relative to each other (for multi-line text) - * - * Since: 0.10.15 - **/ - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT, - g_param_spec_enum ("line-alignment", "line alignment", - "Alignment of text lines relative to each other.", - GST_TYPE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstTextOverlay:silent - * - * If set, no text is rendered. Useful to switch off text rendering - * temporarily without removing the textoverlay element from the pipeline. - * - * Since: 0.10.15 - **/ - /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */ - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT, - g_param_spec_boolean ("silent", "silent", - "Whether to render the text string", - DEFAULT_PROP_SILENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstTextOverlay:wait-text - * - * If set, the video will block until a subtitle is received on the text pad. - * If video and subtitles are sent in sync, like from the same demuxer, this - * property should be set. - * - * Since: 0.10.20 - **/ - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT, - g_param_spec_boolean ("wait-text", "Wait Text", - "Whether to wait for subtitles", - DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), - PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize", - "Automatically adjust font size to screen-size.", - DEFAULT_PROP_AUTO_ADJUST_SIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER, - g_param_spec_boolean ("vertical-render", "vertical render", - "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_text_overlay_finalize (GObject * object) -{ - GstTextOverlay *overlay = GST_TEXT_OVERLAY (object); - - g_free (overlay->default_text); - - if (overlay->text_image) { - g_free (overlay->text_image); - overlay->text_image = NULL; - } - - if (overlay->layout) { - g_object_unref (overlay->layout); - overlay->layout = NULL; - } - - if (overlay->text_buffer) { - gst_buffer_unref (overlay->text_buffer); - overlay->text_buffer = NULL; - } - - if (overlay->cond) { - g_cond_free (overlay->cond); - overlay->cond = NULL; - } - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass) -{ - GstPadTemplate *template; - PangoFontDescription *desc; - - /* video sink */ - template = gst_static_pad_template_get (&video_sink_template_factory); - overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink"); - gst_object_unref (template); - 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_pad_set_event_function (overlay->video_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_video_event)); - gst_pad_set_chain_function (overlay->video_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_video_chain)); - gst_pad_set_bufferalloc_function (overlay->video_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_video_bufferalloc)); - gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad); - - if (!GST_IS_TIME_OVERLAY_CLASS (klass) && !GST_IS_CLOCK_OVERLAY_CLASS (klass)) { - /* text sink */ - template = gst_static_pad_template_get (&text_sink_template_factory); - overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink"); - gst_object_unref (template); - gst_pad_set_setcaps_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps_txt)); - gst_pad_set_event_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_text_event)); - gst_pad_set_chain_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_text_chain)); - gst_pad_set_link_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_link)); - gst_pad_set_unlink_function (overlay->text_sinkpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_unlink)); - gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad); - } - - /* (video) source */ - template = gst_static_pad_template_get (&src_template_factory); - overlay->srcpad = gst_pad_new_from_template (template, "src"); - gst_object_unref (template); - gst_pad_set_getcaps_function (overlay->srcpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps)); - gst_pad_set_event_function (overlay->srcpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_src_event)); - gst_pad_set_query_function (overlay->srcpad, - GST_DEBUG_FUNCPTR (gst_text_overlay_src_query)); - gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad); - - overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT; - overlay->layout = - pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context); - desc = - pango_context_get_font_description (GST_TEXT_OVERLAY_GET_CLASS - (overlay)->pango_context); - gst_text_overlay_adjust_values_with_fontdesc (overlay, desc); - - overlay->halign = DEFAULT_PROP_HALIGNMENT; - overlay->valign = DEFAULT_PROP_VALIGNMENT; - overlay->xpad = DEFAULT_PROP_XPAD; - overlay->ypad = DEFAULT_PROP_YPAD; - overlay->deltax = DEFAULT_PROP_DELTAX; - overlay->deltay = DEFAULT_PROP_DELTAY; - - overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE; - - overlay->want_shading = DEFAULT_PROP_SHADING; - overlay->shading_value = DEFAULT_SHADING_VALUE; - overlay->silent = DEFAULT_PROP_SILENT; - overlay->wait_text = DEFAULT_PROP_WAIT_TEXT; - overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE; - - overlay->default_text = g_strdup (DEFAULT_PROP_TEXT); - overlay->need_render = TRUE; - overlay->text_image = NULL; - overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER; - gst_text_overlay_update_render_mode (overlay); - - overlay->fps_n = 0; - overlay->fps_d = 1; - - overlay->text_buffer = NULL; - overlay->text_linked = FALSE; - overlay->cond = g_cond_new (); - gst_segment_init (&overlay->segment, GST_FORMAT_TIME); -} - -static void -gst_text_overlay_update_wrap_mode (GstTextOverlay * overlay) -{ - 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 { - int width; - - if (overlay->auto_adjust_size) { - width = DEFAULT_SCALE_BASIS * PANGO_SCALE; - if (overlay->use_vertical_render) { - width = width * (overlay->height - overlay->ypad * 2) / overlay->width; - } - } else { - width = - (overlay->use_vertical_render ? overlay->height : overlay->width) * - PANGO_SCALE; - } - - 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, width); - pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode); - } -} - -static void -gst_text_overlay_update_render_mode (GstTextOverlay * overlay) -{ - PangoMatrix matrix = PANGO_MATRIX_INIT; - PangoContext *context = pango_layout_get_context (overlay->layout); - - if (overlay->use_vertical_render) { - pango_matrix_rotate (&matrix, -90); - pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO); - pango_context_set_matrix (context, &matrix); - pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT); - } else { - pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH); - pango_context_set_matrix (context, &matrix); - pango_layout_set_alignment (overlay->layout, overlay->line_align); - } -} - -static gboolean -gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps) -{ - GstTextOverlay *overlay; - GstStructure *structure; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - structure = gst_caps_get_structure (caps, 0); - overlay->have_pango_markup = - gst_structure_has_name (structure, "text/x-pango-markup"); - - gst_object_unref (overlay); - - return TRUE; -} - -/* FIXME: upstream nego (e.g. when the video window is resized) */ - -static gboolean -gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps) -{ - 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_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - overlay->width = 0; - overlay->height = 0; - structure = gst_caps_get_structure (caps, 0); - fps = gst_structure_get_value (structure, "framerate"); - - if (fps - && gst_video_format_parse_caps (caps, &overlay->format, &overlay->width, - &overlay->height)) { - 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); - } - - gst_object_unref (overlay); - - return ret; -} - -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 PROP_TEXT: - g_free (overlay->default_text); - overlay->default_text = g_value_dup_string (value); - overlay->need_render = TRUE; - break; - case PROP_SHADING: - overlay->want_shading = g_value_get_boolean (value); - break; - case PROP_XPAD: - overlay->xpad = g_value_get_int (value); - break; - case PROP_YPAD: - overlay->ypad = g_value_get_int (value); - break; - case PROP_DELTAX: - overlay->deltax = g_value_get_int (value); - break; - case PROP_DELTAY: - overlay->deltay = g_value_get_int (value); - break; - case PROP_HALIGN:{ - const gchar *s = g_value_get_string (value); - - if (s && g_ascii_strcasecmp (s, "left") == 0) - overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT; - else if (s && g_ascii_strcasecmp (s, "center") == 0) - overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER; - else if (s && g_ascii_strcasecmp (s, "right") == 0) - overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT; - else - g_warning ("Invalid value '%s' for textoverlay property 'halign'", - GST_STR_NULL (s)); - break; - } - case PROP_VALIGN:{ - const gchar *s = g_value_get_string (value); - - if (s && g_ascii_strcasecmp (s, "baseline") == 0) - overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE; - else if (s && g_ascii_strcasecmp (s, "bottom") == 0) - overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM; - else if (s && g_ascii_strcasecmp (s, "top") == 0) - overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP; - else - g_warning ("Invalid value '%s' for textoverlay property 'valign'", - GST_STR_NULL (s)); - break; - } - case PROP_VALIGNMENT: - overlay->valign = g_value_get_enum (value); - break; - case PROP_HALIGNMENT: - overlay->halign = g_value_get_enum (value); - break; - case PROP_WRAP_MODE: - overlay->wrap_mode = g_value_get_enum (value); - gst_text_overlay_update_wrap_mode (overlay); - break; - case PROP_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_OBJECT (overlay, "font description set: %s", fontdesc_str); - pango_layout_set_font_description (overlay->layout, desc); - gst_text_overlay_adjust_values_with_fontdesc (overlay, desc); - pango_font_description_free (desc); - } else { - GST_WARNING_OBJECT (overlay, "font description parse failed: %s", - fontdesc_str); - } - break; - } - case PROP_SILENT: - overlay->silent = g_value_get_boolean (value); - break; - case PROP_LINE_ALIGNMENT: - overlay->line_align = g_value_get_enum (value); - pango_layout_set_alignment (overlay->layout, - (PangoAlignment) overlay->line_align); - break; - case PROP_WAIT_TEXT: - overlay->wait_text = g_value_get_boolean (value); - break; - case PROP_AUTO_ADJUST_SIZE: - { - overlay->auto_adjust_size = g_value_get_boolean (value); - overlay->need_render = TRUE; - } - case PROP_VERTICAL_RENDER: - overlay->use_vertical_render = g_value_get_boolean (value); - gst_text_overlay_update_render_mode (overlay); - overlay->need_render = TRUE; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - overlay->need_render = TRUE; - GST_OBJECT_UNLOCK (overlay); -} - -static void -gst_text_overlay_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstTextOverlay *overlay = GST_TEXT_OVERLAY (object); - - GST_OBJECT_LOCK (overlay); - switch (prop_id) { - case PROP_TEXT: - g_value_set_string (value, overlay->default_text); - break; - case PROP_SHADING: - g_value_set_boolean (value, overlay->want_shading); - break; - case PROP_XPAD: - g_value_set_int (value, overlay->xpad); - break; - case PROP_YPAD: - g_value_set_int (value, overlay->ypad); - break; - case PROP_DELTAX: - g_value_set_int (value, overlay->deltax); - break; - case PROP_DELTAY: - g_value_set_int (value, overlay->deltay); - break; - case PROP_VALIGNMENT: - g_value_set_enum (value, overlay->valign); - break; - case PROP_HALIGNMENT: - g_value_set_enum (value, overlay->halign); - break; - case PROP_WRAP_MODE: - g_value_set_enum (value, overlay->wrap_mode); - break; - case PROP_SILENT: - g_value_set_boolean (value, overlay->silent); - break; - case PROP_LINE_ALIGNMENT: - g_value_set_enum (value, overlay->line_align); - break; - case PROP_WAIT_TEXT: - g_value_set_boolean (value, overlay->wait_text); - break; - case PROP_AUTO_ADJUST_SIZE: - g_value_set_boolean (value, overlay->auto_adjust_size); - break; - case PROP_VERTICAL_RENDER: - g_value_set_boolean (value, overlay->use_vertical_render); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - overlay->need_render = TRUE; - GST_OBJECT_UNLOCK (overlay); -} - -static gboolean -gst_text_overlay_src_query (GstPad * pad, GstQuery * query) -{ - gboolean ret = FALSE; - GstTextOverlay *overlay = NULL; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - ret = gst_pad_peer_query (overlay->video_sinkpad, query); - - gst_object_unref (overlay); - - return ret; -} - -static gboolean -gst_text_overlay_src_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstTextOverlay *overlay = NULL; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK:{ - GstSeekFlags flags; - - /* We don't handle seek if we have not text pad */ - if (!overlay->text_linked) { - GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream"); - ret = gst_pad_push_event (overlay->video_sinkpad, event); - goto beach; - } - - GST_DEBUG_OBJECT (overlay, "seek received, driving from here"); - - gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL); - - /* Flush downstream, only for flushing seek */ - if (flags & GST_SEEK_FLAG_FLUSH) - gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ()); - - /* Mark ourself as flushing, unblock chains */ - GST_OBJECT_LOCK (overlay); - overlay->video_flushing = TRUE; - overlay->text_flushing = TRUE; - gst_text_overlay_pop_text (overlay); - GST_OBJECT_UNLOCK (overlay); - - /* Seek on each sink pad */ - gst_event_ref (event); - ret = gst_pad_push_event (overlay->video_sinkpad, event); - if (ret) { - ret = gst_pad_push_event (overlay->text_sinkpad, event); - } else { - gst_event_unref (event); - } - break; - } - default: - if (overlay->text_linked) { - gst_event_ref (event); - ret = gst_pad_push_event (overlay->video_sinkpad, event); - gst_pad_push_event (overlay->text_sinkpad, event); - } else { - ret = gst_pad_push_event (overlay->video_sinkpad, event); - } - break; - } - -beach: - gst_object_unref (overlay); - - return ret; -} - -static GstCaps * -gst_text_overlay_getcaps (GstPad * pad) -{ - GstTextOverlay *overlay; - GstPad *otherpad; - GstCaps *caps; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - 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)); - } - - GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps); - - gst_object_unref (overlay); - - return caps; -} - -static void -gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * overlay, - PangoFontDescription * desc) -{ - gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE; - overlay->shadow_offset = (double) (font_size) / 13.0; - overlay->outline_offset = (double) (font_size) / 15.0; - if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET) - overlay->outline_offset = MINIMUM_OUTLINE_OFFSET; -} - -#define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \ - b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \ - g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \ - r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \ -} G_STMT_END - -static inline void -gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest, gint xpos, - gint ypos, guchar * text_image, guint dest_stride) -{ - gint i, j = 0; - gint x, y; - guchar r, g, b, a; - guchar *pimage; - guchar *py; - gint width = overlay->image_width; - gint height = overlay->image_height; - - if (xpos < 0) { - xpos = 0; - } - - if (xpos + width > overlay->width) { - width = overlay->width - xpos; - } - - if (ypos + height > overlay->height) { - height = overlay->height - ypos; - } - - dest += (ypos / 1) * dest_stride; - - for (i = 0; i < height; i++) { - pimage = text_image + 4 * (i * overlay->image_width); - py = dest + i * dest_stride + xpos; - for (j = 0; j < width; j++) { - b = pimage[CAIRO_ARGB_B]; - g = pimage[CAIRO_ARGB_G]; - r = pimage[CAIRO_ARGB_R]; - a = pimage[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a, r, g, b); - - pimage += 4; - if (a == 0) { - py++; - continue; - } - COMP_Y (y, r, g, b); - x = *py; - BLEND (*py++, a, y, x); - } - } -} - -static inline void -gst_text_overlay_blit_sub2x2cbcr (GstTextOverlay * overlay, - guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image, - guint destcb_stride, guint destcr_stride) -{ - gint i, j; - gint x, cb, cr; - gushort r, g, b, a; - gushort r1, g1, b1, a1; - guchar *pimage1, *pimage2; - guchar *pcb, *pcr; - gint width = overlay->image_width - 2; - gint height = overlay->image_height - 2; - - if (xpos < 0) { - xpos = 0; - } - - if (xpos + width > overlay->width) { - width = overlay->width - xpos; - } - - if (ypos + height > overlay->height) { - height = overlay->height - ypos; - } - - destcb += (ypos / 2) * destcb_stride; - destcr += (ypos / 2) * destcr_stride; - - for (i = 0; i < height; i += 2) { - pimage1 = text_image + 4 * (i * overlay->image_width); - pimage2 = pimage1 + 4 * overlay->image_width; - pcb = destcb + (i / 2) * destcb_stride + xpos / 2; - pcr = destcr + (i / 2) * destcr_stride + xpos / 2; - for (j = 0; j < width; j += 2) { - b = pimage1[CAIRO_ARGB_B]; - g = pimage1[CAIRO_ARGB_G]; - r = pimage1[CAIRO_ARGB_R]; - a = pimage1[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a, r, g, b); - pimage1 += 4; - - b1 = pimage1[CAIRO_ARGB_B]; - g1 = pimage1[CAIRO_ARGB_G]; - r1 = pimage1[CAIRO_ARGB_R]; - a1 = pimage1[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a1, r1, g1, b1); - b += b1; - g += g1; - r += r1; - a += a1; - pimage1 += 4; - - b1 = pimage2[CAIRO_ARGB_B]; - g1 = pimage2[CAIRO_ARGB_G]; - r1 = pimage2[CAIRO_ARGB_R]; - a1 = pimage2[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a1, r1, g1, b1); - b += b1; - g += g1; - r += r1; - a += a1; - pimage2 += 4; - - /* + 2 for rounding */ - b1 = pimage2[CAIRO_ARGB_B]; - g1 = pimage2[CAIRO_ARGB_G]; - r1 = pimage2[CAIRO_ARGB_R]; - a1 = pimage2[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a1, r1, g1, b1); - b += b1 + 2; - g += g1 + 2; - r += r1 + 2; - a += a1 + 2; - pimage2 += 4; - - b /= 4; - g /= 4; - r /= 4; - a /= 4; - - if (a == 0) { - pcb++; - pcr++; - continue; - } - COMP_U (cb, r, g, b); - COMP_V (cr, r, g, b); - - x = *pcb; - BLEND (*pcb++, a, cb, x); - x = *pcr; - BLEND (*pcr++, a, cr, x); - } - } -} - -static void -gst_text_overlay_render_pangocairo (GstTextOverlay * overlay, - const gchar * string, gint textlen) -{ - cairo_t *cr; - cairo_surface_t *surface; - PangoRectangle ink_rect, logical_rect; - cairo_matrix_t cairo_matrix; - int width, height; - double scalef = 1.0; - - if (overlay->auto_adjust_size) { - /* 640 pixel is default */ - scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS; - } - pango_layout_set_width (overlay->layout, -1); - /* set text on pango layout */ - pango_layout_set_markup (overlay->layout, string, textlen); - - /* get subtitle image size */ - pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); - - width = (logical_rect.width + overlay->shadow_offset) * scalef; - - if (width + overlay->deltax > - (overlay->use_vertical_render ? overlay->height : overlay->width)) { - /* - * subtitle image width is larger then overlay width - * so rearrange overlay wrap mode. - */ - gst_text_overlay_update_wrap_mode (overlay); - pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); - width = overlay->width; - } - - height = - (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef; - if (height > overlay->height) { - height = overlay->height; - } - if (overlay->use_vertical_render) { - PangoRectangle rect; - PangoContext *context; - PangoMatrix matrix = PANGO_MATRIX_INIT; - int tmp; - - context = pango_layout_get_context (overlay->layout); - - pango_matrix_rotate (&matrix, -90); - - rect.x = rect.y = 0; - rect.width = width; - rect.height = height; - pango_matrix_transform_pixel_rectangle (&matrix, &rect); - matrix.x0 = -rect.x; - matrix.y0 = -rect.y; - - pango_context_set_matrix (context, &matrix); - - cairo_matrix.xx = matrix.xx; - cairo_matrix.yx = matrix.yx; - cairo_matrix.xy = matrix.xy; - cairo_matrix.yy = matrix.yy; - cairo_matrix.x0 = matrix.x0; - cairo_matrix.y0 = matrix.y0; - cairo_matrix_scale (&cairo_matrix, scalef, scalef); - - tmp = height; - height = width; - width = tmp; - } else { - cairo_matrix_init_scale (&cairo_matrix, scalef, scalef); - } - - /* reallocate surface */ - overlay->text_image = g_realloc (overlay->text_image, 4 * width * height); - - surface = cairo_image_surface_create_for_data (overlay->text_image, - CAIRO_FORMAT_ARGB32, width, height, width * 4); - cr = cairo_create (surface); - - /* clear surface */ - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr); - - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - - if (overlay->want_shading) - cairo_paint_with_alpha (cr, overlay->shading_value); - - /* apply transformations */ - cairo_set_matrix (cr, &cairo_matrix); - - /* FIXME: We use show_layout everywhere except for the surface - * because it's really faster and internally does all kinds of - * caching. Unfortunately we have to paint to a cairo path for - * the outline and this is slow. Once Pango supports user fonts - * we should use them, see - * https://bugzilla.gnome.org/show_bug.cgi?id=598695 - * - * Idea would the be, to create a cairo user font that - * does shadow, outline, text painting in the - * render_glyph function. - */ - - /* draw shadow text */ - cairo_save (cr); - cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset); - cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5); - pango_cairo_show_layout (cr, overlay->layout); - cairo_restore (cr); - - /* draw outline text */ - cairo_save (cr); - cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); - cairo_set_line_width (cr, overlay->outline_offset); - pango_cairo_layout_path (cr, overlay->layout); - cairo_stroke (cr); - cairo_restore (cr); - - /* draw text */ - cairo_save (cr); - cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); - pango_cairo_show_layout (cr, overlay->layout); - cairo_restore (cr); - - cairo_destroy (cr); - cairo_surface_destroy (surface); - overlay->image_width = width; - overlay->image_height = height; - overlay->baseline_y = ink_rect.y; -} - -#define BOX_XPAD 6 -#define BOX_YPAD 6 - -static inline void -gst_text_overlay_shade_I420_y (GstTextOverlay * overlay, guchar * dest, - gint x0, gint x1, gint y0, gint y1) -{ - gint i, j, dest_stride; - - dest_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, - overlay->width); - - 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); - } - } -} - -static inline void -gst_text_overlay_shade_UYVY_y (GstTextOverlay * overlay, guchar * dest, - gint x0, gint x1, gint y0, gint y1) -{ - gint i, j; - guint dest_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_UYVY, 0, - overlay->width); - - 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; - gint y_pos; - - y_pos = (i * dest_stride) + j * 2 + 1; - y = dest[y_pos] + overlay->shading_value; - - dest[y_pos] = CLAMP (y, 0, 255); - } - } -} - -#define gst_text_overlay_shade_BGRx gst_text_overlay_shade_xRGB -static inline void -gst_text_overlay_shade_xRGB (GstTextOverlay * overlay, guchar * dest, - 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, y_pos, k; - - y_pos = (i * 4 * overlay->width) + j * 4; - for (k = 0; k < 4; k++) { - y = dest[y_pos + k] + overlay->shading_value; - dest[y_pos + k] = 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_I420 (GstTextOverlay * overlay, - guint8 * yuv_pixels, gint xpos, gint ypos) -{ - int y_stride, u_stride, v_stride; - int u_offset, v_offset; - int h, w; - - w = overlay->width; - h = overlay->height; - - y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, w); - u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, w); - v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, w); - u_offset = - gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1, w, h); - v_offset = - gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h); - - gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image, - y_stride); - gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset, - yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride, - v_stride); -} - -static inline void -gst_text_overlay_blit_UYVY (GstTextOverlay * overlay, - guint8 * yuv_pixels, gint xpos, gint ypos) -{ - int a0, r0, g0, b0; - int a1, r1, g1, b1; - int y0, y1, u, v; - int i, j; - int h, w; - guchar *pimage, *dest; - - w = overlay->image_width - 2; - h = overlay->image_height - 2; - - if (xpos < 0) { - xpos = 0; - } - - if (xpos + w > overlay->width) { - w = overlay->width - xpos; - } - - if (ypos + h > overlay->height) { - h = overlay->height - ypos; - } - - for (i = 0; i < h; i++) { - pimage = overlay->text_image + i * overlay->image_width * 4; - dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2; - for (j = 0; j < w; j += 2) { - b0 = pimage[CAIRO_ARGB_B]; - g0 = pimage[CAIRO_ARGB_G]; - r0 = pimage[CAIRO_ARGB_R]; - a0 = pimage[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a0, r0, g0, b0); - pimage += 4; - - b1 = pimage[CAIRO_ARGB_B]; - g1 = pimage[CAIRO_ARGB_G]; - r1 = pimage[CAIRO_ARGB_R]; - a1 = pimage[CAIRO_ARGB_A]; - CAIRO_UNPREMULTIPLY (a1, r1, g1, b1); - pimage += 4; - - a0 += a1 + 2; - a0 /= 2; - if (a0 == 0) { - dest += 4; - continue; - } - - COMP_Y (y0, r0, g0, b0); - COMP_Y (y1, r1, g1, b1); - - b0 += b1 + 2; - g0 += g1 + 2; - r0 += r1 + 2; - - b0 /= 2; - g0 /= 2; - r0 /= 2; - - COMP_U (u, r0, g0, b0); - COMP_V (v, r0, g0, b0); - - BLEND (*dest, a0, u, *dest); - dest++; - BLEND (*dest, a0, y0, *dest); - dest++; - BLEND (*dest, a0, v, *dest); - dest++; - BLEND (*dest, a0, y1, *dest); - dest++; - } - } -} - -#define xRGB_BLIT_FUNCTION(name, R, G, B) \ -static inline void \ -gst_text_overlay_blit_##name (GstTextOverlay * overlay, \ - guint8 * rgb_pixels, gint xpos, gint ypos) \ -{ \ - int a, r, g, b; \ - int i, j; \ - int h, w; \ - guchar *pimage, *dest; \ - \ - w = overlay->image_width; \ - h = overlay->image_height; \ - \ - if (xpos < 0) { \ - xpos = 0; \ - } \ - \ - if (xpos + w > overlay->width) { \ - w = overlay->width - xpos; \ - } \ - \ - if (ypos + h > overlay->height) { \ - h = overlay->height - ypos; \ - } \ - \ - for (i = 0; i < h; i++) { \ - pimage = overlay->text_image + i * overlay->image_width * 4; \ - dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \ - for (j = 0; j < w; j++) { \ - a = pimage[CAIRO_ARGB_A]; \ - b = pimage[CAIRO_ARGB_B]; \ - g = pimage[CAIRO_ARGB_G]; \ - r = pimage[CAIRO_ARGB_R]; \ - CAIRO_UNPREMULTIPLY (a, r, g, b); \ - b = (b*a + dest[B] * (255-a)) / 255; \ - g = (g*a + dest[G] * (255-a)) / 255; \ - r = (r*a + dest[R] * (255-a)) / 255; \ - \ - dest[B] = b; \ - dest[G] = g; \ - dest[R] = r; \ - pimage += 4; \ - dest += 4; \ - } \ - } \ -} -xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3); -xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0); - -static void -gst_text_overlay_render_text (GstTextOverlay * overlay, - const gchar * text, gint textlen) -{ - gchar *string; - - if (!overlay->need_render) { - GST_DEBUG ("Using previously rendered text."); - return; - } - - /* -1 is the whole string */ - if (text != NULL && textlen < 0) { - textlen = strlen (text); - } - - if (text != NULL) { - string = g_strndup (text, textlen); - } else { /* empty string */ - string = g_strdup (" "); - } - g_strdelimit (string, "\r\t", ' '); - textlen = strlen (string); - - /* FIXME: should we check for UTF-8 here? */ - - GST_DEBUG ("Rendering '%s'", string); - gst_text_overlay_render_pangocairo (overlay, string, textlen); - - g_free (string); - - overlay->need_render = FALSE; -} - -static GstFlowReturn -gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame) -{ - gint xpos, ypos; - gint width, height; - GstTextOverlayVAlign valign; - GstTextOverlayHAlign halign; - - width = overlay->image_width; - height = overlay->image_height; - - video_frame = gst_buffer_make_writable (video_frame); - - if (overlay->use_vertical_render) - halign = GST_TEXT_OVERLAY_HALIGN_RIGHT; - else - halign = overlay->halign; - - switch (halign) { - case GST_TEXT_OVERLAY_HALIGN_LEFT: - xpos = overlay->xpad; - break; - case GST_TEXT_OVERLAY_HALIGN_CENTER: - xpos = (overlay->width - width) / 2; - break; - case GST_TEXT_OVERLAY_HALIGN_RIGHT: - xpos = overlay->width - width - overlay->xpad; - break; - default: - xpos = 0; - } - xpos += overlay->deltax; - - if (overlay->use_vertical_render) - valign = GST_TEXT_OVERLAY_VALIGN_TOP; - else - valign = overlay->valign; - - switch (valign) { - case GST_TEXT_OVERLAY_VALIGN_BOTTOM: - ypos = overlay->height - height - overlay->ypad; - break; - case GST_TEXT_OVERLAY_VALIGN_BASELINE: - ypos = overlay->height - (height + 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) { - switch (overlay->format) { - case GST_VIDEO_FORMAT_I420: - gst_text_overlay_shade_I420_y (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, - ypos, ypos + overlay->image_height); - break; - case GST_VIDEO_FORMAT_UYVY: - gst_text_overlay_shade_UYVY_y (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, - ypos, ypos + overlay->image_height); - break; - case GST_VIDEO_FORMAT_xRGB: - gst_text_overlay_shade_xRGB (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, - ypos, ypos + overlay->image_height); - break; - case GST_VIDEO_FORMAT_BGRx: - gst_text_overlay_shade_BGRx (overlay, - GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width, - ypos, ypos + overlay->image_height); - break; - default: - g_assert_not_reached (); - } - } - - if (ypos < 0) - ypos = 0; - - if (overlay->text_image) { - switch (overlay->format) { - case GST_VIDEO_FORMAT_I420: - gst_text_overlay_blit_I420 (overlay, - GST_BUFFER_DATA (video_frame), xpos, ypos); - break; - case GST_VIDEO_FORMAT_UYVY: - gst_text_overlay_blit_UYVY (overlay, - GST_BUFFER_DATA (video_frame), xpos, ypos); - break; - case GST_VIDEO_FORMAT_BGRx: - gst_text_overlay_blit_BGRx (overlay, - GST_BUFFER_DATA (video_frame), xpos, ypos); - break; - case GST_VIDEO_FORMAT_xRGB: - gst_text_overlay_blit_xRGB (overlay, - GST_BUFFER_DATA (video_frame), xpos, ypos); - break; - default: - g_assert_not_reached (); - } - } - return gst_pad_push (overlay->srcpad, video_frame); -} - -static GstPadLinkReturn -gst_text_overlay_text_pad_link (GstPad * pad, GstPad * peer) -{ - GstTextOverlay *overlay; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (overlay, "Text pad linked"); - - overlay->text_linked = TRUE; - - gst_object_unref (overlay); - - return GST_PAD_LINK_OK; -} - -static void -gst_text_overlay_text_pad_unlink (GstPad * pad) -{ - GstTextOverlay *overlay; - - /* 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"); - - overlay->text_linked = FALSE; - - gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED); -} - -static gboolean -gst_text_overlay_text_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstTextOverlay *overlay = NULL; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT:{ - GstFormat fmt; - gboolean update; - gdouble rate, applied_rate; - gint64 cur, stop, time; - - overlay->text_eos = FALSE; - - gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, - &fmt, &cur, &stop, &time); - - if (fmt == GST_FORMAT_TIME) { - GST_OBJECT_LOCK (overlay); - gst_segment_set_newsegment_full (&overlay->text_segment, update, rate, - applied_rate, GST_FORMAT_TIME, cur, stop, time); - GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT, - &overlay->text_segment); - GST_OBJECT_UNLOCK (overlay); - } else { - GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL), - ("received non-TIME newsegment event on text input")); - } - - gst_event_unref (event); - ret = TRUE; - - /* wake up the video chain, it might be waiting for a text buffer or - * a text segment update */ - GST_OBJECT_LOCK (overlay); - GST_TEXT_OVERLAY_BROADCAST (overlay); - GST_OBJECT_UNLOCK (overlay); - break; - } - case GST_EVENT_FLUSH_STOP: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "text flush stop"); - overlay->text_flushing = FALSE; - overlay->text_eos = FALSE; - gst_text_overlay_pop_text (overlay); - gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME); - GST_OBJECT_UNLOCK (overlay); - gst_event_unref (event); - ret = TRUE; - break; - case GST_EVENT_FLUSH_START: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "text flush start"); - overlay->text_flushing = TRUE; - GST_TEXT_OVERLAY_BROADCAST (overlay); - GST_OBJECT_UNLOCK (overlay); - gst_event_unref (event); - ret = TRUE; - break; - case GST_EVENT_EOS: - GST_OBJECT_LOCK (overlay); - overlay->text_eos = TRUE; - GST_INFO_OBJECT (overlay, "text EOS"); - /* wake up the video chain, it might be waiting for a text buffer or - * a text segment update */ - GST_TEXT_OVERLAY_BROADCAST (overlay); - GST_OBJECT_UNLOCK (overlay); - gst_event_unref (event); - ret = TRUE; - break; - default: - ret = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (overlay); - - return ret; -} - -static gboolean -gst_text_overlay_video_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstTextOverlay *overlay = NULL; - - overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate; - gint64 start, stop, time; - gboolean update; - - GST_DEBUG_OBJECT (overlay, "received new segment"); - - gst_event_parse_new_segment (event, &update, &rate, &format, &start, - &stop, &time); - - if (format == GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT, - &overlay->segment); - - gst_segment_set_newsegment (&overlay->segment, update, rate, format, - start, stop, time); - } else { - GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL), - ("received non-TIME newsegment event on video input")); - } - - ret = gst_pad_event_default (pad, event); - break; - } - case GST_EVENT_EOS: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "video EOS"); - overlay->video_eos = TRUE; - GST_OBJECT_UNLOCK (overlay); - ret = gst_pad_event_default (pad, event); - break; - case GST_EVENT_FLUSH_START: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "video flush start"); - overlay->video_flushing = TRUE; - GST_TEXT_OVERLAY_BROADCAST (overlay); - GST_OBJECT_UNLOCK (overlay); - ret = gst_pad_event_default (pad, event); - break; - case GST_EVENT_FLUSH_STOP: - GST_OBJECT_LOCK (overlay); - GST_INFO_OBJECT (overlay, "video flush stop"); - overlay->video_flushing = FALSE; - overlay->video_eos = FALSE; - gst_segment_init (&overlay->segment, GST_FORMAT_TIME); - GST_OBJECT_UNLOCK (overlay); - ret = gst_pad_event_default (pad, event); - break; - default: - ret = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (overlay); - - return ret; -} - -static GstFlowReturn -gst_text_overlay_video_bufferalloc (GstPad * pad, guint64 offset, guint size, - GstCaps * caps, GstBuffer ** buffer) -{ - GstTextOverlay *overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad)); - GstFlowReturn ret = GST_FLOW_WRONG_STATE; - GstPad *allocpad; - - GST_OBJECT_LOCK (overlay); - allocpad = overlay->srcpad ? gst_object_ref (overlay->srcpad) : NULL; - GST_OBJECT_UNLOCK (overlay); - - if (allocpad) { - ret = gst_pad_alloc_buffer (allocpad, offset, size, caps, buffer); - gst_object_unref (allocpad); - } - - gst_object_unref (overlay); - return ret; -} - -/* Called with lock held */ -static void -gst_text_overlay_pop_text (GstTextOverlay * overlay) -{ - g_return_if_fail (GST_IS_TEXT_OVERLAY (overlay)); - - if (overlay->text_buffer) { - GST_DEBUG_OBJECT (overlay, "releasing text buffer %p", - overlay->text_buffer); - gst_buffer_unref (overlay->text_buffer); - overlay->text_buffer = NULL; - } - - /* Let the text task know we used that buffer */ - GST_TEXT_OVERLAY_BROADCAST (overlay); -} - -/* We receive text buffers here. If they are out of segment we just ignore them. - If the buffer is in our segment we keep it internally except if another one - is already waiting here, in that case we wait that it gets kicked out */ -static GstFlowReturn -gst_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstTextOverlay *overlay = NULL; - gboolean in_seg = FALSE; - gint64 clip_start = 0, clip_stop = 0; - - overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad)); - - GST_OBJECT_LOCK (overlay); - - if (overlay->text_flushing) { - GST_OBJECT_UNLOCK (overlay); - ret = GST_FLOW_WRONG_STATE; - GST_LOG_OBJECT (overlay, "text flushing"); - goto beach; - } - - if (overlay->text_eos) { - GST_OBJECT_UNLOCK (overlay); - ret = GST_FLOW_UNEXPECTED; - GST_LOG_OBJECT (overlay, "text EOS"); - goto beach; - } - - GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%" - GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) + - GST_BUFFER_DURATION (buffer))); - - if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) { - GstClockTime stop; - - if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer))) - stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer); - else - stop = GST_CLOCK_TIME_NONE; - - in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME, - GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop); - } else { - in_seg = TRUE; - } - - if (in_seg) { - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - GST_BUFFER_TIMESTAMP (buffer) = clip_start; - else if (GST_BUFFER_DURATION_IS_VALID (buffer)) - GST_BUFFER_DURATION (buffer) = clip_stop - clip_start; - - /* Wait for the previous buffer to go away */ - while (overlay->text_buffer != NULL) { - GST_DEBUG ("Pad %s:%s has a buffer queued, waiting", - GST_DEBUG_PAD_NAME (pad)); - GST_TEXT_OVERLAY_WAIT (overlay); - GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad)); - if (overlay->text_flushing) { - GST_OBJECT_UNLOCK (overlay); - ret = GST_FLOW_WRONG_STATE; - goto beach; - } - } - - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - gst_segment_set_last_stop (&overlay->text_segment, GST_FORMAT_TIME, - clip_start); - - overlay->text_buffer = buffer; - /* That's a new text buffer we need to render */ - overlay->need_render = TRUE; - - /* in case the video chain is waiting for a text buffer, wake it up */ - GST_TEXT_OVERLAY_BROADCAST (overlay); - } - - GST_OBJECT_UNLOCK (overlay); - -beach: - - return ret; -} - -static GstFlowReturn -gst_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer) -{ - GstTextOverlayClass *klass; - GstTextOverlay *overlay; - GstFlowReturn ret = GST_FLOW_OK; - gboolean in_seg = FALSE; - gint64 start, stop, clip_start = 0, clip_stop = 0; - gchar *text = NULL; - - overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad)); - klass = GST_TEXT_OVERLAY_GET_CLASS (overlay); - - if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) - goto missing_timestamp; - - /* ignore buffers that are outside of the current segment */ - start = GST_BUFFER_TIMESTAMP (buffer); - - if (!GST_BUFFER_DURATION_IS_VALID (buffer)) { - stop = GST_CLOCK_TIME_NONE; - } else { - stop = start + GST_BUFFER_DURATION (buffer); - } - - GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%" - GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment, - GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); - - /* segment_clip() will adjust start unconditionally to segment_start if - * no stop time is provided, so handle this ourselves */ - if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start) - goto out_of_segment; - - in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop, - &clip_start, &clip_stop); - - if (!in_seg) - goto out_of_segment; - - /* if the buffer is only partially in the segment, fix up stamps */ - if (clip_start != start || (stop != -1 && clip_stop != stop)) { - GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment"); - buffer = gst_buffer_make_metadata_writable (buffer); - GST_BUFFER_TIMESTAMP (buffer) = clip_start; - if (stop != -1) - GST_BUFFER_DURATION (buffer) = clip_stop - clip_start; - } - - /* now, after we've done the clipping, fix up end time if there's no - * duration (we only use those estimated values internally though, we - * don't want to set bogus values on the buffer itself) */ - if (stop == -1) { - GstStructure *s; - gint fps_num, fps_denom; - - s = gst_caps_get_structure (GST_PAD_CAPS (pad), 0); - if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) && - fps_num && fps_denom) { - GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate"); - stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num); - } else { - GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration"); - stop = start + 1; /* we need to assume some interval */ - } - } - -wait_for_text_buf: - - GST_OBJECT_LOCK (overlay); - - if (overlay->video_flushing) - goto flushing; - - if (overlay->video_eos) - goto have_eos; - - if (overlay->silent) { - GST_OBJECT_UNLOCK (overlay); - ret = gst_pad_push (overlay->srcpad, buffer); - - /* Update last_stop */ - gst_segment_set_last_stop (&overlay->segment, GST_FORMAT_TIME, clip_start); - - return ret; - } - - /* Text pad not linked, rendering internal text */ - if (!overlay->text_linked) { - if (klass->get_text) { - text = klass->get_text (overlay, buffer); - } else { - text = g_strdup (overlay->default_text); - } - - GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default " - "text: '%s'", GST_STR_NULL (text)); - - GST_OBJECT_UNLOCK (overlay); - - if (text != NULL && *text != '\0') { - /* Render and push */ - gst_text_overlay_render_text (overlay, text, -1); - ret = gst_text_overlay_push_frame (overlay, buffer); - } else { - /* Invalid or empty string */ - ret = gst_pad_push (overlay->srcpad, buffer); - } - } else { - /* Text pad linked, check if we have a text buffer queued */ - if (overlay->text_buffer) { - gboolean pop_text = FALSE, valid_text_time = TRUE; - GstClockTime text_start = GST_CLOCK_TIME_NONE; - GstClockTime text_end = GST_CLOCK_TIME_NONE; - GstClockTime text_running_time = GST_CLOCK_TIME_NONE; - GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE; - GstClockTime vid_running_time, vid_running_time_end; - - /* 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_IS_VALID (overlay->text_buffer) || - !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) { - GST_WARNING_OBJECT (overlay, - "Got text buffer with invalid timestamp or duration"); - pop_text = TRUE; - valid_text_time = FALSE; - } else { - text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer); - text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer); - } - - vid_running_time = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - start); - vid_running_time_end = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - stop); - - /* If timestamp and duration are valid */ - if (valid_text_time) { - text_running_time = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - text_start); - text_running_time_end = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - text_end); - } - - GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT, - GST_TIME_ARGS (text_running_time), - GST_TIME_ARGS (text_running_time_end)); - GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT, - GST_TIME_ARGS (vid_running_time), - GST_TIME_ARGS (vid_running_time_end)); - - /* Text too old or in the future */ - if (valid_text_time && text_running_time_end <= vid_running_time) { - /* text buffer too old, get rid of it and do nothing */ - GST_LOG_OBJECT (overlay, "text buffer too old, popping"); - pop_text = FALSE; - gst_text_overlay_pop_text (overlay); - GST_OBJECT_UNLOCK (overlay); - goto wait_for_text_buf; - } else if (valid_text_time && vid_running_time_end <= text_running_time) { - GST_LOG_OBJECT (overlay, "text in future, pushing video buf"); - GST_OBJECT_UNLOCK (overlay); - /* Push the video frame */ - ret = gst_pad_push (overlay->srcpad, buffer); - } else { - gchar *in_text; - gsize in_size; - - in_text = (gchar *) GST_BUFFER_DATA (overlay->text_buffer); - in_size = GST_BUFFER_SIZE (overlay->text_buffer); - - /* g_markup_escape_text() absolutely requires valid UTF8 input, it - * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING - * here on purpose, this is something that needs fixing upstream */ - if (!g_utf8_validate (in_text, in_size, NULL)) { - const gchar *end = NULL; - - GST_WARNING_OBJECT (overlay, "received invalid UTF-8"); - in_text = g_strndup (in_text, in_size); - while (!g_utf8_validate (in_text, in_size, &end) && end) - *((gchar *) end) = '*'; - } - - /* Get the string */ - if (overlay->have_pango_markup) { - text = g_strndup (in_text, in_size); - } else { - text = g_markup_escape_text (in_text, in_size); - } - - if (text != NULL && *text != '\0') { - gint text_len = strlen (text); - - while (text_len > 0 && (text[text_len - 1] == '\n' || - text[text_len - 1] == '\r')) { - --text_len; - } - GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text); - gst_text_overlay_render_text (overlay, text, text_len); - } else { - GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)"); - gst_text_overlay_render_text (overlay, " ", 1); - } - - if (in_text != (gchar *) GST_BUFFER_DATA (overlay->text_buffer)) - g_free (in_text); - - GST_OBJECT_UNLOCK (overlay); - ret = gst_text_overlay_push_frame (overlay, buffer); - - if (valid_text_time && text_running_time_end <= vid_running_time_end) { - GST_LOG_OBJECT (overlay, "text buffer not needed any longer"); - pop_text = TRUE; - } - } - if (pop_text) { - GST_OBJECT_LOCK (overlay); - gst_text_overlay_pop_text (overlay); - GST_OBJECT_UNLOCK (overlay); - } - } else { - gboolean wait_for_text_buf = TRUE; - - if (overlay->text_eos) - wait_for_text_buf = FALSE; - - if (!overlay->wait_text) - wait_for_text_buf = FALSE; - - /* Text pad linked, but no text buffer available - what now? */ - if (overlay->text_segment.format == GST_FORMAT_TIME) { - GstClockTime text_start_running_time, text_last_stop_running_time; - GstClockTime vid_running_time; - - vid_running_time = - gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME, - GST_BUFFER_TIMESTAMP (buffer)); - text_start_running_time = - gst_segment_to_running_time (&overlay->text_segment, - GST_FORMAT_TIME, overlay->text_segment.start); - text_last_stop_running_time = - gst_segment_to_running_time (&overlay->text_segment, - GST_FORMAT_TIME, overlay->text_segment.last_stop); - - if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) && - vid_running_time < text_start_running_time) || - (GST_CLOCK_TIME_IS_VALID (text_last_stop_running_time) && - vid_running_time < text_last_stop_running_time)) { - wait_for_text_buf = FALSE; - } - } - - if (wait_for_text_buf) { - GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one"); - GST_TEXT_OVERLAY_WAIT (overlay); - GST_DEBUG_OBJECT (overlay, "resuming"); - GST_OBJECT_UNLOCK (overlay); - goto wait_for_text_buf; - } else { - GST_OBJECT_UNLOCK (overlay); - GST_LOG_OBJECT (overlay, "no need to wait for a text buffer"); - ret = gst_pad_push (overlay->srcpad, buffer); - } - } - } - - g_free (text); - - /* Update last_stop */ - gst_segment_set_last_stop (&overlay->segment, GST_FORMAT_TIME, clip_start); - - return ret; - -missing_timestamp: - { - GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding"); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - -flushing: - { - GST_OBJECT_UNLOCK (overlay); - GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer"); - gst_buffer_unref (buffer); - return GST_FLOW_WRONG_STATE; - } -have_eos: - { - GST_OBJECT_UNLOCK (overlay); - GST_DEBUG_OBJECT (overlay, "eos, discarding buffer"); - gst_buffer_unref (buffer); - return GST_FLOW_UNEXPECTED; - } -out_of_segment: - { - GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding"); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } -} - -static GstStateChangeReturn -gst_text_overlay_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstTextOverlay *overlay = GST_TEXT_OVERLAY (element); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_OBJECT_LOCK (overlay); - overlay->text_flushing = TRUE; - overlay->video_flushing = TRUE; - /* pop_text will broadcast on the GCond and thus also make the video - * chain exit if it's waiting for a text buffer */ - gst_text_overlay_pop_text (overlay); - GST_OBJECT_UNLOCK (overlay); - break; - default: - break; - } - - ret = parent_class->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_OBJECT_LOCK (overlay); - overlay->text_flushing = FALSE; - overlay->video_flushing = FALSE; - overlay->video_eos = FALSE; - overlay->text_eos = FALSE; - gst_segment_init (&overlay->segment, GST_FORMAT_TIME); - gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME); - GST_OBJECT_UNLOCK (overlay); - break; - default: - break; - } - - return ret; -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE, - 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); */ - - GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements"); - - return TRUE; -} - -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 deleted file mode 100644 index 4590b270..00000000 --- a/ext/pango/gsttextoverlay.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef __GST_TEXT_OVERLAY_H__ -#define __GST_TEXT_OVERLAY_H__ - -#include <gst/gst.h> -#include <gst/video/video.h> -#include <pango/pangocairo.h> - -G_BEGIN_DECLS - -#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; - -/** - * GstTextOverlayVAlign: - * @GST_TEXT_OVERLAY_VALIGN_BASELINE: draw text on the baseline - * @GST_TEXT_OVERLAY_VALIGN_BOTTOM: draw text on the bottom - * @GST_TEXT_OVERLAY_VALIGN_TOP: draw test on top - * - * Vertical alignment of the text. - */ -typedef enum { - GST_TEXT_OVERLAY_VALIGN_BASELINE, - GST_TEXT_OVERLAY_VALIGN_BOTTOM, - GST_TEXT_OVERLAY_VALIGN_TOP -} GstTextOverlayVAlign; - -/** - * GstTextOverlayHAlign: - * @GST_TEXT_OVERLAY_HALIGN_LEFT: align text left - * @GST_TEXT_OVERLAY_HALIGN_CENTER: align text center - * @GST_TEXT_OVERLAY_HALIGN_RIGHT: align text right - * - * Horizontal alignment of the text. - */ -typedef enum { - GST_TEXT_OVERLAY_HALIGN_LEFT, - GST_TEXT_OVERLAY_HALIGN_CENTER, - GST_TEXT_OVERLAY_HALIGN_RIGHT -} GstTextOverlayHAlign; - -/** - * GstTextOverlayWrapMode: - * @GST_TEXT_OVERLAY_WRAP_MODE_NONE: no wrapping - * @GST_TEXT_OVERLAY_WRAP_MODE_WORD: do word wrapping - * @GST_TEXT_OVERLAY_WRAP_MODE_CHAR: do char wrapping - * @GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR: do word and char wrapping - * - * Whether to wrap the text and if so how. - */ -typedef enum { - 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 -} GstTextOverlayWrapMode; - -/** - * GstTextOverlayLineAlign: - * @GST_TEXT_OVERLAY_LINE_ALIGN_LEFT: lines are left-aligned - * @GST_TEXT_OVERLAY_LINE_ALIGN_CENTER: lines are center-aligned - * @GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT: lines are right-aligned - * - * Alignment of text lines relative to each other - */ -typedef enum { - GST_TEXT_OVERLAY_LINE_ALIGN_LEFT = PANGO_ALIGN_LEFT, - GST_TEXT_OVERLAY_LINE_ALIGN_CENTER = PANGO_ALIGN_CENTER, - GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT = PANGO_ALIGN_RIGHT -} GstTextOverlayLineAlign; - -/** - * GstTextOverlay: - * - * Opaque textoverlay object structure - */ -struct _GstTextOverlay { - GstElement element; - - GstPad *video_sinkpad; - GstPad *text_sinkpad; - GstPad *srcpad; - - GstSegment segment; - GstSegment text_segment; - GstBuffer *text_buffer; - gboolean text_linked; - gboolean video_flushing; - gboolean video_eos; - gboolean text_flushing; - gboolean text_eos; - - GCond *cond; /* to signal removal of a queued text - * buffer, arrival of a text buffer, - * a text segment update, or a change - * in status (e.g. shutdown, flushing) */ - - gint width; - gint height; - gint fps_n; - gint fps_d; - GstVideoFormat format; - - GstTextOverlayVAlign valign; - GstTextOverlayHAlign halign; - GstTextOverlayWrapMode wrap_mode; - GstTextOverlayLineAlign line_align; - - gint xpad; - gint ypad; - gint deltax; - gint deltay; - gchar *default_text; - gboolean want_shading; - gboolean silent; - gboolean wait_text; - - PangoLayout *layout; - gdouble shadow_offset; - gdouble outline_offset; - guchar *text_image; - gint image_width; - gint image_height; - gint baseline_y; - - gboolean auto_adjust_size; - gboolean need_render; - - gint shading_value; /* for timeoverlay subclass */ - - gboolean have_pango_markup; - gboolean use_vertical_render; -}; - -struct _GstTextOverlayClass { - GstElementClass parent_class; - - PangoContext *pango_context; - - gchar * (*get_text) (GstTextOverlay *overlay, GstBuffer *video_frame); -}; - -GType gst_text_overlay_get_type(void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GST_TEXT_OVERLAY_H */ diff --git a/ext/pango/gsttextrender.c b/ext/pango/gsttextrender.c deleted file mode 100644 index c9372449..00000000 --- a/ext/pango/gsttextrender.c +++ /dev/null @@ -1,705 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) <2003> David Schleef <ds@schleef.org> - * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com> - * - * 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-textrender - * @see_also: #GstTextOverlay - * - * This plugin renders text received on the text sink pad to a video - * buffer (retaining the alpha channel), so it can later be overlayed - * on top of video streams using other elements. - * - * The text can contain newline characters. (FIXME: What about text - * wrapping? It does not make sense in this context) - * - * <refsect2> - * <title>Example launch lines</title> - * |[ - * gst-launch -v filesrc location=subtitles.srt ! subparse ! textrender ! xvimagesink - * ]| - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <gst/gst.h> -#include <gst/video/video.h> - -#include "gsttextrender.h" -#include <string.h> - -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -# define CAIRO_ARGB_A 3 -# define CAIRO_ARGB_R 2 -# define CAIRO_ARGB_G 1 -# define CAIRO_ARGB_B 0 -#else -# define CAIRO_ARGB_A 0 -# define CAIRO_ARGB_R 1 -# define CAIRO_ARGB_G 2 -# define CAIRO_ARGB_B 3 -#endif - -GST_DEBUG_CATEGORY_EXTERN (pango_debug); -#define GST_CAT_DEFAULT pango_debug - -#define MINIMUM_OUTLINE_OFFSET 1.0 - -#define DEFAULT_PROP_VALIGNMENT GST_TEXT_RENDER_VALIGN_BASELINE -#define DEFAULT_PROP_HALIGNMENT GST_TEXT_RENDER_HALIGN_CENTER -#define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_RENDER_LINE_ALIGN_CENTER -#define DEFAULT_PROP_XPAD 25 -#define DEFAULT_PROP_YPAD 25 - -#define DEFAULT_RENDER_WIDTH 720 -#define DEFAULT_RENDER_HEIGHT 576 - -enum -{ - PROP_0, - PROP_HALIGNMENT, - PROP_VALIGNMENT, - PROP_LINE_ALIGNMENT, - PROP_XPAD, - PROP_YPAD, - PROP_FONT_DESC -}; - - -static GstStaticPadTemplate src_template_factory = - GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_ARGB) - ); - -static GstStaticPadTemplate sink_template_factory = - GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("text/x-pango-markup; text/plain") - ); - -#define GST_TYPE_TEXT_RENDER_VALIGN (gst_text_render_valign_get_type()) -static GType -gst_text_render_valign_get_type (void) -{ - static GType text_render_valign_type = 0; - static const GEnumValue text_render_valign[] = { - {GST_TEXT_RENDER_VALIGN_BASELINE, "baseline", "baseline"}, - {GST_TEXT_RENDER_VALIGN_BOTTOM, "bottom", "bottom"}, - {GST_TEXT_RENDER_VALIGN_TOP, "top", "top"}, - {0, NULL, NULL}, - }; - - if (!text_render_valign_type) { - text_render_valign_type = - g_enum_register_static ("GstTextRenderVAlign", text_render_valign); - } - return text_render_valign_type; -} - -#define GST_TYPE_TEXT_RENDER_HALIGN (gst_text_render_halign_get_type()) -static GType -gst_text_render_halign_get_type (void) -{ - static GType text_render_halign_type = 0; - static const GEnumValue text_render_halign[] = { - {GST_TEXT_RENDER_HALIGN_LEFT, "left", "left"}, - {GST_TEXT_RENDER_HALIGN_CENTER, "center", "center"}, - {GST_TEXT_RENDER_HALIGN_RIGHT, "right", "right"}, - {0, NULL, NULL}, - }; - - if (!text_render_halign_type) { - text_render_halign_type = - g_enum_register_static ("GstTextRenderHAlign", text_render_halign); - } - return text_render_halign_type; -} - -#define GST_TYPE_TEXT_RENDER_LINE_ALIGN (gst_text_render_line_align_get_type()) -static GType -gst_text_render_line_align_get_type (void) -{ - static GType text_render_line_align_type = 0; - static const GEnumValue text_render_line_align[] = { - {GST_TEXT_RENDER_LINE_ALIGN_LEFT, "left", "left"}, - {GST_TEXT_RENDER_LINE_ALIGN_CENTER, "center", "center"}, - {GST_TEXT_RENDER_LINE_ALIGN_RIGHT, "right", "right"}, - {0, NULL, NULL} - }; - - if (!text_render_line_align_type) { - text_render_line_align_type = - g_enum_register_static ("GstTextRenderLineAlign", - text_render_line_align); - } - return text_render_line_align_type; -} - -static void gst_text_render_adjust_values_with_fontdesc (GstTextRender * - render, PangoFontDescription * desc); - -GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT); - -static void gst_text_render_finalize (GObject * object); -static void gst_text_render_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_text_render_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static void -gst_text_render_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template_factory)); - - gst_element_class_set_details_simple (element_class, "Text renderer", - "Filter/Editor/Video", - "Renders a text string to an image bitmap", - "David Schleef <ds@schleef.org>, " - "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>"); -} - -static void -gst_text_render_class_init (GstTextRenderClass * klass) -{ - GObjectClass *gobject_class; - PangoFontMap *fontmap; - - gobject_class = (GObjectClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_text_render_finalize; - gobject_class->set_property = gst_text_render_set_property; - gobject_class->get_property = gst_text_render_get_property; - - fontmap = pango_cairo_font_map_get_default (); - klass->pango_context = - pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC, - g_param_spec_string ("font-desc", "font description", - "Pango font description of font " - "to be used for rendering. " - "See documentation of " - "pango_font_description_from_string" - " for syntax.", "", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT, - g_param_spec_enum ("valignment", "vertical alignment", - "Vertical alignment of the text", GST_TYPE_TEXT_RENDER_VALIGN, - DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT, - g_param_spec_enum ("halignment", "horizontal alignment", - "Horizontal alignment of the text", GST_TYPE_TEXT_RENDER_HALIGN, - DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD, - g_param_spec_int ("xpad", "horizontal paddding", - "Horizontal paddding when using left/right alignment", 0, G_MAXINT, - DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD, - g_param_spec_int ("ypad", "vertical padding", - "Vertical padding when using top/bottom alignment", 0, G_MAXINT, - DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT, - g_param_spec_enum ("line-alignment", "line alignment", - "Alignment of text lines relative to each other.", - GST_TYPE_TEXT_RENDER_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_text_render_adjust_values_with_fontdesc (GstTextRender * render, - PangoFontDescription * desc) -{ - gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE; - - render->shadow_offset = (double) (font_size) / 13.0; - render->outline_offset = (double) (font_size) / 15.0; - if (render->outline_offset < MINIMUM_OUTLINE_OFFSET) - render->outline_offset = MINIMUM_OUTLINE_OFFSET; -} - -static void -gst_text_render_render_pangocairo (GstTextRender * render) -{ - cairo_t *cr; - cairo_surface_t *surface; - cairo_t *cr_shadow; - cairo_surface_t *surface_shadow; - PangoRectangle ink_rect, logical_rect; - gint width, height; - - pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect); - - width = logical_rect.width + render->shadow_offset; - height = logical_rect.height + logical_rect.y + render->shadow_offset; - - surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); - cr_shadow = cairo_create (surface_shadow); - - /* clear shadow surface */ - cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr_shadow); - cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER); - - /* draw shadow text */ - cairo_save (cr_shadow); - cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5); - cairo_translate (cr_shadow, render->shadow_offset, render->shadow_offset); - pango_cairo_show_layout (cr_shadow, render->layout); - cairo_restore (cr_shadow); - - /* draw outline text */ - cairo_save (cr_shadow); - cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0); - cairo_set_line_width (cr_shadow, render->outline_offset); - pango_cairo_layout_path (cr_shadow, render->layout); - cairo_stroke (cr_shadow); - cairo_restore (cr_shadow); - - cairo_destroy (cr_shadow); - - render->text_image = g_realloc (render->text_image, 4 * width * height); - - surface = cairo_image_surface_create_for_data (render->text_image, - CAIRO_FORMAT_ARGB32, width, height, width * 4); - cr = cairo_create (surface); - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr); - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - - /* set default color */ - cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); - - cairo_save (cr); - /* draw text */ - pango_cairo_show_layout (cr, render->layout); - cairo_restore (cr); - - /* composite shadow with offset */ - cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); - cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0); - cairo_paint (cr); - - cairo_destroy (cr); - cairo_surface_destroy (surface_shadow); - cairo_surface_destroy (surface); - render->image_width = width; - render->image_height = height; -} - -static void -gst_text_render_check_argb (GstTextRender * render) -{ - GstCaps *peer_caps; - peer_caps = gst_pad_get_allowed_caps (render->srcpad); - if (G_LIKELY (peer_caps)) { - guint i = 0, n = 0; - - n = gst_caps_get_size (peer_caps); - GST_DEBUG_OBJECT (render, "peer allowed caps (%u structure(s)) are %" - GST_PTR_FORMAT, n, peer_caps); - - /* Check if AYUV or ARGB is first */ - for (i = 0; i < n; i++) { - GstStructure *s = gst_caps_get_structure (peer_caps, i); - if (gst_structure_has_name (s, "video/x-raw-rgb") && - gst_structure_has_field (s, "alpha_mask")) { - render->use_ARGB = TRUE; - break; - } else if (gst_structure_has_name (s, "video/x-raw-yuv")) { - guint fourcc; - if (gst_structure_get_fourcc (s, "format", &fourcc) && - fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V')) { - render->use_ARGB = FALSE; - break; - } - } - } - gst_caps_unref (peer_caps); - } -} - -static gboolean -gst_text_render_setcaps (GstPad * pad, GstCaps * caps) -{ - GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad)); - GstStructure *structure; - gboolean ret = FALSE; - gint width = 0, height = 0; - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "width", &width); - gst_structure_get_int (structure, "height", &height); - - GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps); - - if (width >= render->image_width && height >= render->image_height) { - render->width = width; - render->height = height; - ret = TRUE; - } - - gst_text_render_check_argb (render); - - gst_object_unref (render); - return ret; -} - -static void -gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps) -{ - GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad)); - GstStructure *s = gst_caps_get_structure (caps, 0); - - GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps); - gst_structure_fixate_field_nearest_int (s, "width", render->image_width); - gst_structure_fixate_field_nearest_int (s, "height", render->image_height); - GST_DEBUG ("Fixated to %" GST_PTR_FORMAT, caps); - - gst_object_unref (render); -} - -#define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \ - b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \ - g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \ - r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \ -} G_STMT_END - -static void -gst_text_renderer_image_to_ayuv (GstTextRender * render, guchar * pixbuf, - int xpos, int ypos, int stride) -{ - int y; /* text bitmap coordinates */ - guchar *p, *bitp; - guchar a, r, g, b; - int width, height; - - width = render->image_width; - height = render->image_height; - bitp = render->text_image; - - for (y = 0; y < height; y++) { - int n; - p = pixbuf + (ypos + y) * stride + xpos * 4; - for (n = 0; n < width; n++) { - b = bitp[CAIRO_ARGB_B]; - g = bitp[CAIRO_ARGB_G]; - r = bitp[CAIRO_ARGB_R]; - a = bitp[CAIRO_ARGB_A]; - bitp += 4; - - /* Cairo uses pre-multiplied ARGB, unpremultiply it */ - CAIRO_UNPREMULTIPLY (a, r, g, b); - - *p++ = a; - *p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + - ((7471 * b) >> 16)), 0, 255); - *p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + - ((32768 * b) >> 16) + 128), 0, 255); - *p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - - ((5329 * b) >> 16) + 128), 0, 255); - } - } -} - -static void -gst_text_renderer_image_to_argb (GstTextRender * render, guchar * pixbuf, - int xpos, int ypos, int stride) -{ - int i, j; - guchar *p, *bitp; - int width, height; - - width = render->image_width; - height = render->image_height; - bitp = render->text_image; - - for (i = 0; i < height; i++) { - p = pixbuf + (ypos + i) * stride + xpos * 4; - for (j = 0; j < width; j++) { - p[0] = bitp[CAIRO_ARGB_A]; - p[1] = bitp[CAIRO_ARGB_R]; - p[2] = bitp[CAIRO_ARGB_G]; - p[3] = bitp[CAIRO_ARGB_B]; - - /* Cairo uses pre-multiplied ARGB, unpremultiply it */ - CAIRO_UNPREMULTIPLY (p[0], p[1], p[2], p[3]); - - bitp += 4; - p += 4; - } - } -} - -static GstFlowReturn -gst_text_render_chain (GstPad * pad, GstBuffer * inbuf) -{ - GstTextRender *render; - GstFlowReturn ret; - GstBuffer *outbuf; - GstCaps *caps = NULL; - guint8 *data = GST_BUFFER_DATA (inbuf); - guint size = GST_BUFFER_SIZE (inbuf); - gint n; - gint xpos, ypos; - - render = GST_TEXT_RENDER (gst_pad_get_parent (pad)); - - /* somehow pango barfs over "\0" buffers... */ - while (size > 0 && - (data[size - 1] == '\r' || - data[size - 1] == '\n' || data[size - 1] == '\0')) { - size--; - } - - /* render text */ - GST_DEBUG ("rendering '%*s'", size, data); - pango_layout_set_markup (render->layout, (gchar *) data, size); - gst_text_render_render_pangocairo (render); - - gst_text_render_check_argb (render); - - if (!render->use_ARGB) { - caps = - gst_video_format_new_caps (GST_VIDEO_FORMAT_AYUV, render->width, - render->height, 1, 1, 1, 1); - } else { - caps = - gst_video_format_new_caps (GST_VIDEO_FORMAT_ARGB, render->width, - render->height, 1, 1, 1, 1); - } - - if (!gst_pad_set_caps (render->srcpad, caps)) { - gst_caps_unref (caps); - GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL)); - ret = GST_FLOW_ERROR; - goto done; - } - - GST_DEBUG ("Allocating buffer WxH = %dx%d", render->width, render->height); - ret = - gst_pad_alloc_buffer_and_set_caps (render->srcpad, GST_BUFFER_OFFSET_NONE, - render->width * render->height * 4, caps, &outbuf); - - if (ret != GST_FLOW_OK) - goto done; - - gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS); - data = GST_BUFFER_DATA (outbuf); - - if (render->use_ARGB) { - memset (data, 0, render->width * render->height * 4); - } else { - for (n = 0; n < render->width * render->height; n++) { - data[n * 4] = data[n * 4 + 1] = 0; - data[n * 4 + 2] = data[n * 4 + 3] = 128; - } - } - - switch (render->halign) { - case GST_TEXT_RENDER_HALIGN_LEFT: - xpos = render->xpad; - break; - case GST_TEXT_RENDER_HALIGN_CENTER: - xpos = (render->width - render->image_width) / 2; - break; - case GST_TEXT_RENDER_HALIGN_RIGHT: - xpos = render->width - render->image_width - render->xpad; - break; - default: - xpos = 0; - } - - switch (render->valign) { - case GST_TEXT_RENDER_VALIGN_BOTTOM: - ypos = render->height - render->image_height - render->ypad; - break; - case GST_TEXT_RENDER_VALIGN_BASELINE: - ypos = render->height - (render->image_height + render->ypad); - break; - case GST_TEXT_RENDER_VALIGN_TOP: - ypos = render->ypad; - break; - default: - ypos = render->ypad; - break; - } - - if (render->text_image) { - if (render->use_ARGB) { - gst_text_renderer_image_to_argb (render, data, xpos, ypos, - render->width * 4); - } else { - gst_text_renderer_image_to_ayuv (render, data, xpos, ypos, - render->width * 4); - } - } - - ret = gst_pad_push (render->srcpad, outbuf); - -done: - if (caps) - gst_caps_unref (caps); - gst_buffer_unref (inbuf); - gst_object_unref (render); - return ret; -} - -static void -gst_text_render_finalize (GObject * object) -{ - GstTextRender *render = GST_TEXT_RENDER (object); - - g_free (render->text_image); - - if (render->layout) - g_object_unref (render->layout); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass) -{ - GstPadTemplate *template; - - /* sink */ - template = gst_static_pad_template_get (&sink_template_factory); - render->sinkpad = gst_pad_new_from_template (template, "sink"); - gst_object_unref (template); - gst_pad_set_chain_function (render->sinkpad, - GST_DEBUG_FUNCPTR (gst_text_render_chain)); - gst_element_add_pad (GST_ELEMENT (render), render->sinkpad); - - /* source */ - template = gst_static_pad_template_get (&src_template_factory); - render->srcpad = gst_pad_new_from_template (template, "src"); - gst_object_unref (template); - gst_pad_set_fixatecaps_function (render->srcpad, - GST_DEBUG_FUNCPTR (gst_text_render_fixate_caps)); - gst_pad_set_setcaps_function (render->srcpad, - GST_DEBUG_FUNCPTR (gst_text_render_setcaps)); - - gst_element_add_pad (GST_ELEMENT (render), render->srcpad); - - render->line_align = DEFAULT_PROP_LINE_ALIGNMENT; - render->layout = - pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context); - pango_layout_set_alignment (render->layout, - (PangoAlignment) render->line_align); - - render->halign = DEFAULT_PROP_HALIGNMENT; - render->valign = DEFAULT_PROP_VALIGNMENT; - render->xpad = DEFAULT_PROP_XPAD; - render->ypad = DEFAULT_PROP_YPAD; - - render->width = DEFAULT_RENDER_WIDTH; - render->height = DEFAULT_RENDER_HEIGHT; - - render->use_ARGB = FALSE; -} - -static void -gst_text_render_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstTextRender *render = GST_TEXT_RENDER (object); - - switch (prop_id) { - case PROP_VALIGNMENT: - render->valign = g_value_get_enum (value); - break; - case PROP_HALIGNMENT: - render->halign = g_value_get_enum (value); - break; - case PROP_LINE_ALIGNMENT: - render->line_align = g_value_get_enum (value); - pango_layout_set_alignment (render->layout, - (PangoAlignment) render->line_align); - break; - case PROP_XPAD: - render->xpad = g_value_get_int (value); - break; - case PROP_YPAD: - render->ypad = g_value_get_int (value); - break; - case PROP_FONT_DESC: - { - PangoFontDescription *desc; - - desc = pango_font_description_from_string (g_value_get_string (value)); - if (desc) { - GST_LOG ("font description set: %s", g_value_get_string (value)); - GST_OBJECT_LOCK (render); - pango_layout_set_font_description (render->layout, desc); - gst_text_render_adjust_values_with_fontdesc (render, desc); - pango_font_description_free (desc); - gst_text_render_render_pangocairo (render); - GST_OBJECT_UNLOCK (render); - } else { - GST_WARNING ("font description parse failed: %s", - g_value_get_string (value)); - } - break; - } - - default: - break; - } -} - -static void -gst_text_render_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstTextRender *render = GST_TEXT_RENDER (object); - - switch (prop_id) { - case PROP_VALIGNMENT: - g_value_set_enum (value, render->valign); - break; - case PROP_HALIGNMENT: - g_value_set_enum (value, render->halign); - break; - case PROP_LINE_ALIGNMENT: - g_value_set_enum (value, render->line_align); - break; - case PROP_XPAD: - g_value_set_int (value, render->xpad); - break; - case PROP_YPAD: - g_value_set_int (value, render->ypad); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/ext/pango/gsttextrender.h b/ext/pango/gsttextrender.h deleted file mode 100644 index 66d37dbf..00000000 --- a/ext/pango/gsttextrender.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef __GST_TEXT_RENDER_H__ -#define __GST_TEXT_RENDER_H__ - -#include <gst/gst.h> -#include <pango/pangocairo.h> - -G_BEGIN_DECLS - -#define GST_TYPE_TEXT_RENDER (gst_text_render_get_type()) -#define GST_TEXT_RENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ - GST_TYPE_TEXT_RENDER, GstTextRender)) -#define GST_TEXT_RENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\ - GST_TYPE_TEXT_RENDER, GstTextRenderClass)) -#define GST_TEXT_RENDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - GST_TYPE_TEXT_RENDER, GstTextRenderClass)) -#define GST_IS_TEXT_RENDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\ - GST_TYPE_TEXT_RENDER)) -#define GST_IS_TEXT_RENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\ - GST_TYPE_TEXT_RENDER)) - -typedef struct _GstTextRender GstTextRender; -typedef struct _GstTextRenderClass GstTextRenderClass; - -/** - * GstTextRenderVAlign: - * @GST_TEXT_RENDER_VALIGN_BASELINE: draw text on the baseline - * @GST_TEXT_RENDER_VALIGN_BOTTOM: draw text on the bottom - * @GST_TEXT_RENDER_VALIGN_TOP: draw test on top - * - * Vertical alignment of the text. - */ -typedef enum { - GST_TEXT_RENDER_VALIGN_BASELINE, - GST_TEXT_RENDER_VALIGN_BOTTOM, - GST_TEXT_RENDER_VALIGN_TOP -} GstTextRenderVAlign; - -/** - * GstTextRenderHAlign: - * @GST_TEXT_RENDER_HALIGN_LEFT: align text left - * @GST_TEXT_RENDER_HALIGN_CENTER: align text center - * @GST_TEXT_RENDER_HALIGN_RIGHT: align text right - * - * Horizontal alignment of the text. - */ -typedef enum { - GST_TEXT_RENDER_HALIGN_LEFT, - GST_TEXT_RENDER_HALIGN_CENTER, - GST_TEXT_RENDER_HALIGN_RIGHT -} GstTextRenderHAlign; - -/** - * GstTextRenderLineAlign: - * @GST_TEXT_RENDER_LINE_ALIGN_LEFT: lines are left-aligned - * @GST_TEXT_RENDER_LINE_ALIGN_CENTER: lines are center-aligned - * @GST_TEXT_RENDER_LINE_ALIGN_RIGHT: lines are right-aligned - * - * Alignment of text lines relative to each other - */ -typedef enum { - GST_TEXT_RENDER_LINE_ALIGN_LEFT = PANGO_ALIGN_LEFT, - GST_TEXT_RENDER_LINE_ALIGN_CENTER = PANGO_ALIGN_CENTER, - GST_TEXT_RENDER_LINE_ALIGN_RIGHT = PANGO_ALIGN_RIGHT -} GstTextRenderLineAlign; - -/** - * GstTextRender: - * - * Opaque textrender data structure. - */ -struct _GstTextRender { - GstElement element; - - GstPad *sinkpad, *srcpad; - gint width; - gint height; - PangoLayout *layout; - gdouble shadow_offset; - gdouble outline_offset; - guchar *text_image; - gint image_width; - gint image_height; - gint baseline_y; - gboolean use_ARGB; - - GstTextRenderVAlign valign; - GstTextRenderHAlign halign; - GstTextRenderLineAlign line_align; - - gint xpad; - gint ypad; -}; - -struct _GstTextRenderClass { - GstElementClass parent_class; - - PangoContext *pango_context; -}; - -GType gst_text_render_get_type(void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GST_TEXT_RENDER_H */ diff --git a/ext/pango/gsttimeoverlay.c b/ext/pango/gsttimeoverlay.c deleted file mode 100644 index a342a51e..00000000 --- a/ext/pango/gsttimeoverlay.c +++ /dev/null @@ -1,151 +0,0 @@ -/* 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. - */ - -/** - * SECTION:element-timeoverlay - * @see_also: #GstTextOverlay, #GstClockOverlay - * - * This element overlays the buffer time stamps of a video stream on - * top of itself. You can position the text and configure the font details - * using the properties of the #GstTextOverlay class. By default, the - * time stamp is displayed in the top left corner of the picture, with some - * padding to the left and to the top. - * - * <refsect2> - * |[ - * gst-launch -v videotestsrc ! timeoverlay ! xvimagesink - * ]| Display the time stamps in the top left - * corner of the video picture. - * |[ - * gst-launch -v videotestsrc ! timeoverlay halign=right valign=bottom text="Stream time:" shaded-background=true ! xvimagesink - * ]| Another pipeline that displays the time stamps with some leading - * text in the bottom right corner of the video picture, with the background - * of the text being shaded in order to make it more legible on top of a - * bright video background. - * </refsect2> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/video/video.h> - -#include <gsttimeoverlay.h> - -GST_BOILERPLATE (GstTimeOverlay, gst_time_overlay, GstTextOverlay, - GST_TYPE_TEXT_OVERLAY); - -static void -gst_time_overlay_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details_simple (element_class, "Time overlay", - "Filter/Editor/Video", - "Overlays buffer time stamps on a video stream", - "Tim-Philipp Müller <tim@centricular.net>"); -} - -static gchar * -gst_time_overlay_render_time (GstTimeOverlay * overlay, GstClockTime time) -{ - guint hours, mins, secs, msecs; - - if (!GST_CLOCK_TIME_IS_VALID (time)) - return g_strdup (""); - - 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)); - - return g_strdup_printf ("%u:%02u:%02u.%03u", hours, mins, secs, msecs); -} - -/* Called with lock held */ -static gchar * -gst_time_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame) -{ - GstClockTime time = GST_BUFFER_TIMESTAMP (video_frame); - gchar *time_str, *txt, *ret; - - overlay->need_render = TRUE; - - if (!GST_CLOCK_TIME_IS_VALID (time)) { - GST_DEBUG ("buffer without valid timestamp"); - return g_strdup (""); - } - - GST_DEBUG ("buffer with timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (time)); - - txt = g_strdup (overlay->default_text); - - 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; - } - - g_free (txt); - g_free (time_str); - - return ret; -} - -static void -gst_time_overlay_class_init (GstTimeOverlayClass * klass) -{ - GstTextOverlayClass *gsttextoverlay_class; - - gsttextoverlay_class = (GstTextOverlayClass *) klass; - - gsttextoverlay_class->get_text = gst_time_overlay_get_text; -} - -static void -gst_time_overlay_init (GstTimeOverlay * overlay, GstTimeOverlayClass * 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_static (font_description, "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/gsttimeoverlay.h b/ext/pango/gsttimeoverlay.h deleted file mode 100644 index 5fbfba97..00000000 --- a/ext/pango/gsttimeoverlay.h +++ /dev/null @@ -1,61 +0,0 @@ -/* 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_TIME_OVERLAY_H__ -#define __GST_TIME_OVERLAY_H__ - -#include "gsttextoverlay.h" - -G_BEGIN_DECLS - -#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; - -/** - * GstTimeOverlay: - * - * Opaque timeoverlay data structure. - */ -struct _GstTimeOverlay { - GstTextOverlay textoverlay; -}; - -struct _GstTimeOverlayClass { - GstTextOverlayClass parent_class; -}; - -GType gst_time_overlay_get_type (void); - -G_END_DECLS - -#endif /* __GST_TIME_OVERLAY_H__ */ - |