2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2003> David Schleef <ds@schleef.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
23 * SECTION:element-textrender
24 * @see_also: #GstTextOverlay
28 * This plugin renders text received on the text sink pad to a video
29 * buffer (retaining the alpha channel), so it can later be overlayed
30 * on top of video streams using other elements.
33 * The text can contain newline characters. (FIXME: What about text
34 * wrapping? It does not make sense in this context)
39 * gst-launch -v filesrc location=subtitles.srt ! subparse ! textrender ! xvimagesink
50 #include <gst/video/video.h>
52 #include "gsttextrender.h"
54 GST_DEBUG_CATEGORY_EXTERN (pango_debug);
55 #define GST_CAT_DEFAULT pango_debug
57 static GstElementDetails text_render_details = {
59 "Filter/Editor/Video",
60 "Renders a text string to an image bitmap",
61 "David Schleef <ds@schleef.org>, "
62 "Ronald S. Bultje <rbultje@ronald.bitfreak.net>"
72 static GstStaticPadTemplate src_template_factory =
73 GST_STATIC_PAD_TEMPLATE ("src",
76 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
79 static GstStaticPadTemplate sink_template_factory =
80 GST_STATIC_PAD_TEMPLATE ("sink",
83 GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
86 GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT)
88 static void gst_text_render_finalize (GObject * object);
89 static void gst_text_render_set_property (GObject * object,
90 guint prop_id, const GValue * value, GParamSpec * pspec);
92 static void gst_text_render_base_init (gpointer g_class)
94 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
96 gst_element_class_add_pad_template (element_class,
97 gst_static_pad_template_get (&src_template_factory));
98 gst_element_class_add_pad_template (element_class,
99 gst_static_pad_template_get (&sink_template_factory));
101 gst_element_class_set_details (element_class, &text_render_details);
105 gst_text_render_class_init (GstTextRenderClass * klass)
107 GObjectClass *gobject_class;
108 GstElementClass *gstelement_class;
110 gobject_class = (GObjectClass *) klass;
111 gstelement_class = (GstElementClass *) klass;
113 parent_class = g_type_class_peek_parent (klass);
115 gobject_class->finalize = gst_text_render_finalize;
116 gobject_class->set_property = gst_text_render_set_property;
118 klass->pango_context = pango_ft2_get_context (72, 72);
119 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC,
120 g_param_spec_string ("font-desc", "font description",
121 "Pango font description of font "
122 "to be used for rendering. "
123 "See documentation of "
124 "pango_font_description_from_string"
125 " for syntax.", "", G_PARAM_WRITABLE));
130 resize_bitmap (GstTextRender * render, gint width, gint height)
132 FT_Bitmap *bitmap = &render->bitmap;
133 gint pitch = (width | 3) + 1;
134 gint size = pitch * height;
136 /* no need to keep reallocating; just keep the maximum size so far */
137 if (size <= render->bitmap_buffer_size) {
138 bitmap->rows = height;
139 bitmap->width = width;
140 bitmap->pitch = pitch;
141 memset (bitmap->buffer, 0, render->bitmap_buffer_size);
144 if (!bitmap->buffer) {
146 bitmap->pixel_mode = ft_pixel_mode_grays;
147 bitmap->num_grays = 256;
150 bitmap->buffer = g_realloc (bitmap->buffer, size);
152 bitmap->buffer = g_malloc (size);
153 bitmap->rows = height;
154 bitmap->width = width;
155 bitmap->pitch = pitch;
156 memset (bitmap->buffer, 0, size);
157 render->bitmap_buffer_size = size;
161 gst_text_render_render_text (GstTextRender * render)
163 PangoRectangle ink_rect, logical_rect;
165 pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect);
166 resize_bitmap (render, ink_rect.width, ink_rect.height + ink_rect.y);
167 pango_ft2_render_layout (&render->bitmap, render->layout, -ink_rect.x, 0);
168 render->baseline_y = ink_rect.y;
172 gst_text_render_setcaps (GstPad * pad, GstCaps * caps)
174 GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
175 GstStructure *structure;
176 gboolean ret = FALSE;
177 gint width = 0, height = 0;
179 structure = gst_caps_get_structure (caps, 0);
180 gst_structure_get_int (structure, "width", &width);
181 gst_structure_get_int (structure, "height", &height);
183 GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps);
185 if (width >= render->bitmap.width && height >= render->bitmap.rows) {
186 render->width = width;
187 render->height = height;
191 gst_object_unref (render);
196 gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps)
198 GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
199 GstStructure *s = gst_caps_get_structure (caps, 0);
201 GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps);
202 gst_structure_fixate_field_nearest_int (s, "width", render->bitmap.width);
203 gst_structure_fixate_field_nearest_int (s, "height", render->bitmap.rows);
204 GST_DEBUG ("Fixated to %" GST_PTR_FORMAT, caps);
206 gst_object_unref (render);
210 gst_text_renderer_bitmap_to_ayuv (GstTextRender * render, FT_Bitmap * bitmap,
213 int y; /* text bitmap coordinates */
214 int rowinc, bit_rowinc;
218 rowinc = render->width - bitmap->width;
219 bit_rowinc = bitmap->pitch - bitmap->width;
221 bitp = bitmap->buffer;
224 for (y = 0; y < bitmap->rows; y++) {
227 for (n = bitmap->width; n > 0; --n) {
245 gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
247 GstTextRender *render;
250 GstCaps *caps = NULL;
251 guint8 *data = GST_BUFFER_DATA (inbuf);
252 guint size = GST_BUFFER_SIZE (inbuf);
255 render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
257 /* somehow pango barfs over "\0" buffers... */
259 (data[size - 1] == '\r' ||
260 data[size - 1] == '\n' || data[size - 1] == '\0')) {
265 GST_DEBUG ("rendering '%*s'", size, data);
266 pango_layout_set_markup (render->layout, (gchar *) data, size);
267 gst_text_render_render_text (render);
269 caps = gst_caps_new_simple ("video/x-raw-yuv", "format", GST_TYPE_FOURCC,
270 GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'), "width", G_TYPE_INT,
271 render->bitmap.width, "height", G_TYPE_INT, render->bitmap.rows,
272 "framerate", GST_TYPE_FRACTION, 1, 1, NULL);
274 if (!gst_pad_set_caps (render->srcpad, caps)) {
275 gst_caps_unref (caps);
276 GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL));
277 ret = GST_FLOW_ERROR;
281 GST_DEBUG ("Allocating AYUV buffer WxH = %dx%d", render->width,
284 gst_pad_alloc_buffer_and_set_caps (render->srcpad, GST_BUFFER_OFFSET_NONE,
285 render->width * render->height * 4, caps, &outbuf);
287 if (ret != GST_FLOW_OK)
290 gst_buffer_stamp (outbuf, inbuf);
291 data = GST_BUFFER_DATA (outbuf);
293 for (n = 0; n < render->width * render->height; n++) {
296 data[n * 4 + 2] = data[n * 4 + 3] = 128;
299 if (render->bitmap.buffer) {
300 gst_text_renderer_bitmap_to_ayuv (render, &render->bitmap, data);
303 ret = gst_pad_push (render->srcpad, outbuf);
307 gst_caps_unref (caps);
308 gst_buffer_unref (inbuf);
309 gst_object_unref (render);
314 gst_text_render_finalize (GObject * object)
316 GstTextRender *render = GST_TEXT_RENDER (object);
318 g_free (render->bitmap.buffer);
321 g_object_unref (render->layout);
323 G_OBJECT_CLASS (parent_class)->finalize (object);
327 gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass)
331 gst_pad_new_from_template (gst_static_pad_template_get
332 (&sink_template_factory), "sink");
333 gst_pad_set_chain_function (render->sinkpad,
334 GST_DEBUG_FUNCPTR (gst_text_render_chain));
335 gst_element_add_pad (GST_ELEMENT (render), render->sinkpad);
339 gst_pad_new_from_template (gst_static_pad_template_get
340 (&src_template_factory), "src");
341 gst_pad_set_fixatecaps_function (render->srcpad,
342 GST_DEBUG_FUNCPTR (gst_text_render_fixate_caps));
343 gst_pad_set_setcaps_function (render->srcpad,
344 GST_DEBUG_FUNCPTR (gst_text_render_setcaps));
345 gst_element_add_pad (GST_ELEMENT (render), render->srcpad);
348 pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context);
349 memset (&render->bitmap, 0, sizeof (render->bitmap));
353 gst_text_render_set_property (GObject * object, guint prop_id,
354 const GValue * value, GParamSpec * pspec)
356 GstTextRender *render = GST_TEXT_RENDER (object);
361 PangoFontDescription *desc;
363 desc = pango_font_description_from_string (g_value_get_string (value));
365 GST_LOG ("font description set: %s", g_value_get_string (value));
366 GST_OBJECT_LOCK (render);
367 pango_layout_set_font_description (render->layout, desc);
368 pango_font_description_free (desc);
369 gst_text_render_render_text (render);
370 GST_OBJECT_UNLOCK (render);
372 GST_WARNING ("font description parse failed: %s",
373 g_value_get_string (value));