1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
4  * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 
23 /**
24  * SECTION:element-textrender
25  * @title: textrender
26  * @see_also: #GstTextOverlay
27  *
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.
31  *
32  * The text can contain newline characters. (FIXME: What about text
33  * wrapping? It does not make sense in this context)
34  *
35  * ## Example launch lines
36  * |[
37  * gst-launch-1.0 -v filesrc location=subtitles.srt ! subparse ! textrender ! videoconvert ! autovideosink
38  * ]|
39  *
40  */
41 
42 #ifdef HAVE_CONFIG_H
43 #include <config.h>
44 #endif
45 
46 #include <gst/gst.h>
47 #include <gst/video/video.h>
48 
49 #include "gsttextrender.h"
50 #include <string.h>
51 
52 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
53 # define CAIRO_ARGB_A 3
54 # define CAIRO_ARGB_R 2
55 # define CAIRO_ARGB_G 1
56 # define CAIRO_ARGB_B 0
57 #else
58 # define CAIRO_ARGB_A 0
59 # define CAIRO_ARGB_R 1
60 # define CAIRO_ARGB_G 2
61 # define CAIRO_ARGB_B 3
62 #endif
63 
64 GST_DEBUG_CATEGORY_EXTERN (pango_debug);
65 #define GST_CAT_DEFAULT pango_debug
66 
67 #define MINIMUM_OUTLINE_OFFSET 1.0
68 
69 #define DEFAULT_PROP_VALIGNMENT GST_TEXT_RENDER_VALIGN_BASELINE
70 #define DEFAULT_PROP_HALIGNMENT GST_TEXT_RENDER_HALIGN_CENTER
71 #define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_RENDER_LINE_ALIGN_CENTER
72 #define DEFAULT_PROP_XPAD       25
73 #define DEFAULT_PROP_YPAD       25
74 
75 #define DEFAULT_RENDER_WIDTH 720
76 #define DEFAULT_RENDER_HEIGHT 576
77 
78 enum
79 {
80   PROP_0,
81   PROP_HALIGNMENT,
82   PROP_VALIGNMENT,
83   PROP_LINE_ALIGNMENT,
84   PROP_XPAD,
85   PROP_YPAD,
86   PROP_FONT_DESC
87 };
88 
89 #define VIDEO_FORMATS "{ AYUV, ARGB } "
90 
91 static GstStaticPadTemplate src_template_factory =
92 GST_STATIC_PAD_TEMPLATE ("src",
93     GST_PAD_SRC,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
96     );
97 
98 static GstStaticPadTemplate sink_template_factory =
99 GST_STATIC_PAD_TEMPLATE ("sink",
100     GST_PAD_SINK,
101     GST_PAD_ALWAYS,
102     GST_STATIC_CAPS ("text/x-raw, format = { pango-markup, utf8 }")
103     );
104 
105 #define GST_TYPE_TEXT_RENDER_VALIGN (gst_text_render_valign_get_type())
106 static GType
gst_text_render_valign_get_type(void)107 gst_text_render_valign_get_type (void)
108 {
109   static GType text_render_valign_type = 0;
110   static const GEnumValue text_render_valign[] = {
111     {GST_TEXT_RENDER_VALIGN_BASELINE, "baseline", "baseline"},
112     {GST_TEXT_RENDER_VALIGN_BOTTOM, "bottom", "bottom"},
113     {GST_TEXT_RENDER_VALIGN_TOP, "top", "top"},
114     {0, NULL, NULL},
115   };
116 
117   if (!text_render_valign_type) {
118     text_render_valign_type =
119         g_enum_register_static ("GstTextRenderVAlign", text_render_valign);
120   }
121   return text_render_valign_type;
122 }
123 
124 #define GST_TYPE_TEXT_RENDER_HALIGN (gst_text_render_halign_get_type())
125 static GType
gst_text_render_halign_get_type(void)126 gst_text_render_halign_get_type (void)
127 {
128   static GType text_render_halign_type = 0;
129   static const GEnumValue text_render_halign[] = {
130     {GST_TEXT_RENDER_HALIGN_LEFT, "left", "left"},
131     {GST_TEXT_RENDER_HALIGN_CENTER, "center", "center"},
132     {GST_TEXT_RENDER_HALIGN_RIGHT, "right", "right"},
133     {0, NULL, NULL},
134   };
135 
136   if (!text_render_halign_type) {
137     text_render_halign_type =
138         g_enum_register_static ("GstTextRenderHAlign", text_render_halign);
139   }
140   return text_render_halign_type;
141 }
142 
143 #define GST_TYPE_TEXT_RENDER_LINE_ALIGN (gst_text_render_line_align_get_type())
144 static GType
gst_text_render_line_align_get_type(void)145 gst_text_render_line_align_get_type (void)
146 {
147   static GType text_render_line_align_type = 0;
148   static const GEnumValue text_render_line_align[] = {
149     {GST_TEXT_RENDER_LINE_ALIGN_LEFT, "left", "left"},
150     {GST_TEXT_RENDER_LINE_ALIGN_CENTER, "center", "center"},
151     {GST_TEXT_RENDER_LINE_ALIGN_RIGHT, "right", "right"},
152     {0, NULL, NULL}
153   };
154 
155   if (!text_render_line_align_type) {
156     text_render_line_align_type =
157         g_enum_register_static ("GstTextRenderLineAlign",
158         text_render_line_align);
159   }
160   return text_render_line_align_type;
161 }
162 
163 static void gst_text_render_adjust_values_with_fontdesc (GstTextRender *
164     render, PangoFontDescription * desc);
165 
166 #define gst_text_render_parent_class parent_class
167 G_DEFINE_TYPE (GstTextRender, gst_text_render, GST_TYPE_ELEMENT);
168 
169 static void gst_text_render_finalize (GObject * object);
170 static void gst_text_render_set_property (GObject * object,
171     guint prop_id, const GValue * value, GParamSpec * pspec);
172 static void gst_text_render_get_property (GObject * object,
173     guint prop_id, GValue * value, GParamSpec * pspec);
174 
175 static void
gst_text_render_class_init(GstTextRenderClass * klass)176 gst_text_render_class_init (GstTextRenderClass * klass)
177 {
178   GObjectClass *gobject_class;
179   GstElementClass *gstelement_class;
180 
181   gobject_class = (GObjectClass *) klass;
182   gstelement_class = (GstElementClass *) klass;
183 
184   parent_class = g_type_class_peek_parent (klass);
185 
186   gobject_class->finalize = gst_text_render_finalize;
187   gobject_class->set_property = gst_text_render_set_property;
188   gobject_class->get_property = gst_text_render_get_property;
189 
190   gst_element_class_add_static_pad_template (gstelement_class,
191       &src_template_factory);
192   gst_element_class_add_static_pad_template (gstelement_class,
193       &sink_template_factory);
194 
195   gst_element_class_set_static_metadata (gstelement_class, "Text renderer",
196       "Filter/Editor/Video",
197       "Renders a text string to an image bitmap",
198       "David Schleef <ds@schleef.org>, "
199       "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
200 
201   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
202       g_param_spec_string ("font-desc", "font description",
203           "Pango font description of font "
204           "to be used for rendering. "
205           "See documentation of "
206           "pango_font_description_from_string"
207           " for syntax.", "", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
208   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
209       g_param_spec_enum ("valignment", "vertical alignment",
210           "Vertical alignment of the text", GST_TYPE_TEXT_RENDER_VALIGN,
211           DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
213       g_param_spec_enum ("halignment", "horizontal alignment",
214           "Horizontal alignment of the text", GST_TYPE_TEXT_RENDER_HALIGN,
215           DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
216   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
217       g_param_spec_int ("xpad", "horizontal paddding",
218           "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
219           DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
220   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
221       g_param_spec_int ("ypad", "vertical padding",
222           "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
223           DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
224   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
225       g_param_spec_enum ("line-alignment", "line alignment",
226           "Alignment of text lines relative to each other.",
227           GST_TYPE_TEXT_RENDER_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
228           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
229 }
230 
231 static void
gst_text_render_adjust_values_with_fontdesc(GstTextRender * render,PangoFontDescription * desc)232 gst_text_render_adjust_values_with_fontdesc (GstTextRender * render,
233     PangoFontDescription * desc)
234 {
235   gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
236 
237   render->shadow_offset = (double) (font_size) / 13.0;
238   render->outline_offset = (double) (font_size) / 15.0;
239   if (render->outline_offset < MINIMUM_OUTLINE_OFFSET)
240     render->outline_offset = MINIMUM_OUTLINE_OFFSET;
241 }
242 
243 static void
gst_text_render_render_pangocairo(GstTextRender * render)244 gst_text_render_render_pangocairo (GstTextRender * render)
245 {
246   cairo_t *cr;
247   cairo_surface_t *surface;
248   cairo_t *cr_shadow;
249   cairo_surface_t *surface_shadow;
250   PangoRectangle ink_rect, logical_rect;
251   gint width, height;
252 
253   pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect);
254 
255   width = logical_rect.width + render->shadow_offset;
256   height = logical_rect.height + logical_rect.y + render->shadow_offset;
257 
258   surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
259   cr_shadow = cairo_create (surface_shadow);
260 
261   /* clear shadow surface */
262   cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR);
263   cairo_paint (cr_shadow);
264   cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER);
265 
266   /* draw shadow text */
267   cairo_save (cr_shadow);
268   cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5);
269   cairo_translate (cr_shadow, render->shadow_offset, render->shadow_offset);
270   pango_cairo_show_layout (cr_shadow, render->layout);
271   cairo_restore (cr_shadow);
272 
273   /* draw outline text */
274   cairo_save (cr_shadow);
275   cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0);
276   cairo_set_line_width (cr_shadow, render->outline_offset);
277   pango_cairo_layout_path (cr_shadow, render->layout);
278   cairo_stroke (cr_shadow);
279   cairo_restore (cr_shadow);
280 
281   cairo_destroy (cr_shadow);
282 
283   render->text_image = g_realloc (render->text_image, 4 * width * height);
284 
285   surface = cairo_image_surface_create_for_data (render->text_image,
286       CAIRO_FORMAT_ARGB32, width, height, width * 4);
287   cr = cairo_create (surface);
288   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
289   cairo_paint (cr);
290   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
291 
292   /* set default color */
293   cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
294 
295   cairo_save (cr);
296   /* draw text */
297   pango_cairo_show_layout (cr, render->layout);
298   cairo_restore (cr);
299 
300   /* composite shadow with offset */
301   cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
302   cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0);
303   cairo_paint (cr);
304 
305   cairo_destroy (cr);
306   cairo_surface_destroy (surface_shadow);
307   cairo_surface_destroy (surface);
308   render->image_width = width;
309   render->image_height = height;
310 }
311 
312 static void
gst_text_render_check_argb(GstTextRender * render)313 gst_text_render_check_argb (GstTextRender * render)
314 {
315   GstCaps *peer_caps;
316   peer_caps = gst_pad_get_allowed_caps (render->srcpad);
317   if (G_LIKELY (peer_caps)) {
318     guint i = 0, n = 0;
319 
320     n = gst_caps_get_size (peer_caps);
321     GST_DEBUG_OBJECT (render, "peer allowed caps (%u structure(s)) are %"
322         GST_PTR_FORMAT, n, peer_caps);
323 
324     /* Check if AYUV or ARGB is first */
325     for (i = 0; i < n; i++) {
326       GstStructure *s;
327       GstVideoFormat vformat;
328       const GstVideoFormatInfo *info;
329       const gchar *fmt;
330 
331       s = gst_caps_get_structure (peer_caps, i);
332       if (!gst_structure_has_name (s, "video/x-raw"))
333         continue;
334 
335       fmt = gst_structure_get_string (s, "format");
336       if (fmt == NULL)
337         continue;
338 
339       vformat = gst_video_format_from_string (fmt);
340       info = gst_video_format_get_info (vformat);
341       if (info == NULL)
342         continue;
343 
344       render->use_ARGB = GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info);
345     }
346     gst_caps_unref (peer_caps);
347   }
348 }
349 
350 static gboolean
gst_text_render_src_setcaps(GstTextRender * render,GstCaps * caps)351 gst_text_render_src_setcaps (GstTextRender * render, GstCaps * caps)
352 {
353   GstStructure *structure;
354   gboolean ret;
355   gint width = 0, height = 0;
356 
357   structure = gst_caps_get_structure (caps, 0);
358   gst_structure_get_int (structure, "width", &width);
359   gst_structure_get_int (structure, "height", &height);
360 
361   GST_DEBUG_OBJECT (render, "Got caps %" GST_PTR_FORMAT, caps);
362 
363   if (width >= render->image_width && height >= render->image_height) {
364     render->width = width;
365     render->height = height;
366   }
367 
368   gst_text_render_check_argb (render);
369 
370   ret = gst_pad_set_caps (render->srcpad, caps);
371 
372   return ret;
373 }
374 
375 static GstCaps *
gst_text_render_fixate_caps(GstTextRender * render,GstCaps * caps)376 gst_text_render_fixate_caps (GstTextRender * render, GstCaps * caps)
377 {
378   GstStructure *s;
379 
380   caps = gst_caps_truncate (caps);
381 
382   caps = gst_caps_make_writable (caps);
383   s = gst_caps_get_structure (caps, 0);
384 
385   GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps);
386   gst_structure_fixate_field_nearest_int (s, "width", MAX (render->image_width,
387           DEFAULT_RENDER_WIDTH));
388   gst_structure_fixate_field_nearest_int (s, "height",
389       MAX (render->image_height + render->ypad, DEFAULT_RENDER_HEIGHT));
390   caps = gst_caps_fixate (caps);
391   GST_DEBUG ("Fixated to    %" GST_PTR_FORMAT, caps);
392 
393   return caps;
394 }
395 
396 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
397   b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
398   g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
399   r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
400 } G_STMT_END
401 
402 static void
gst_text_renderer_image_to_ayuv(GstTextRender * render,guchar * pixbuf,int xpos,int ypos,int stride)403 gst_text_renderer_image_to_ayuv (GstTextRender * render, guchar * pixbuf,
404     int xpos, int ypos, int stride)
405 {
406   int y;                        /* text bitmap coordinates */
407   guchar *p, *bitp;
408   guchar a, r, g, b;
409   int width, height;
410 
411   width = render->image_width;
412   height = render->image_height;
413 
414   for (y = 0; y < height && ypos + y < render->height; y++) {
415     int n;
416     p = pixbuf + (ypos + y) * stride + xpos * 4;
417     bitp = render->text_image + y * width * 4;
418     for (n = 0; n < width && n < render->width; n++) {
419       b = bitp[CAIRO_ARGB_B];
420       g = bitp[CAIRO_ARGB_G];
421       r = bitp[CAIRO_ARGB_R];
422       a = bitp[CAIRO_ARGB_A];
423       bitp += 4;
424 
425       /* Cairo uses pre-multiplied ARGB, unpremultiply it */
426       CAIRO_UNPREMULTIPLY (a, r, g, b);
427 
428       *p++ = a;
429       *p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) +
430               ((7471 * b) >> 16)), 0, 255);
431       *p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) +
432               ((32768 * b) >> 16) + 128), 0, 255);
433       *p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) -
434               ((5329 * b) >> 16) + 128), 0, 255);
435     }
436   }
437 }
438 
439 static void
gst_text_renderer_image_to_argb(GstTextRender * render,guchar * pixbuf,int xpos,int ypos,int stride)440 gst_text_renderer_image_to_argb (GstTextRender * render, guchar * pixbuf,
441     int xpos, int ypos, int stride)
442 {
443   int i, j;
444   guchar *p, *bitp;
445   int width, height;
446 
447   width = render->image_width;
448   height = render->image_height;
449 
450   for (i = 0; i < height && ypos + i < render->height; i++) {
451     p = pixbuf + (ypos + i) * stride + xpos * 4;
452     bitp = render->text_image + i * width * 4;
453     for (j = 0; j < width && j < render->width; j++) {
454       p[0] = bitp[CAIRO_ARGB_A];
455       p[1] = bitp[CAIRO_ARGB_R];
456       p[2] = bitp[CAIRO_ARGB_G];
457       p[3] = bitp[CAIRO_ARGB_B];
458 
459       /* Cairo uses pre-multiplied ARGB, unpremultiply it */
460       CAIRO_UNPREMULTIPLY (p[0], p[1], p[2], p[3]);
461 
462       bitp += 4;
463       p += 4;
464     }
465   }
466 }
467 
468 static GstFlowReturn
gst_text_render_chain(GstPad * pad,GstObject * parent,GstBuffer * inbuf)469 gst_text_render_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf)
470 {
471   GstTextRender *render;
472   GstFlowReturn ret;
473   GstBuffer *outbuf;
474   GstCaps *caps = NULL, *padcaps;
475   GstMapInfo map;
476   guint8 *data;
477   gsize size;
478   gint n;
479   gint xpos, ypos;
480 
481   render = GST_TEXT_RENDER (parent);
482 
483   gst_buffer_map (inbuf, &map, GST_MAP_READ);
484   data = map.data;
485   size = map.size;
486 
487   /* somehow pango barfs over "\0" buffers... */
488   while (size > 0 &&
489       (data[size - 1] == '\r' ||
490           data[size - 1] == '\n' || data[size - 1] == '\0')) {
491     size--;
492   }
493 
494   /* render text */
495   GST_DEBUG ("rendering '%*s'", (gint) size, data);
496   pango_layout_set_markup (render->layout, (gchar *) data, size);
497   gst_text_render_render_pangocairo (render);
498   gst_buffer_unmap (inbuf, &map);
499 
500   gst_text_render_check_argb (render);
501 
502   padcaps = gst_pad_query_caps (render->srcpad, NULL);
503   caps = gst_pad_peer_query_caps (render->srcpad, padcaps);
504   gst_caps_unref (padcaps);
505 
506   if (!caps || gst_caps_is_empty (caps)) {
507     GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL));
508     ret = GST_FLOW_ERROR;
509     goto done;
510   }
511 
512   caps = gst_text_render_fixate_caps (render, caps);
513 
514   if (!gst_text_render_src_setcaps (render, caps)) {
515     GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL));
516     ret = GST_FLOW_ERROR;
517     goto done;
518   }
519 
520   if (render->segment_event) {
521     gst_pad_push_event (render->srcpad, render->segment_event);
522     render->segment_event = NULL;
523   }
524 
525   GST_DEBUG ("Allocating buffer WxH = %dx%d", render->width, render->height);
526   outbuf = gst_buffer_new_and_alloc (render->width * render->height * 4);
527 
528   gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
529 
530   gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
531   data = map.data;
532   size = map.size;
533 
534   if (render->use_ARGB) {
535     memset (data, 0, render->width * render->height * 4);
536   } else {
537     for (n = 0; n < render->width * render->height; n++) {
538       data[n * 4] = data[n * 4 + 1] = 0;
539       data[n * 4 + 2] = data[n * 4 + 3] = 128;
540     }
541   }
542 
543   switch (render->halign) {
544     case GST_TEXT_RENDER_HALIGN_LEFT:
545       xpos = render->xpad;
546       break;
547     case GST_TEXT_RENDER_HALIGN_CENTER:
548       xpos = (render->width - render->image_width) / 2;
549       break;
550     case GST_TEXT_RENDER_HALIGN_RIGHT:
551       xpos = render->width - render->image_width - render->xpad;
552       break;
553     default:
554       xpos = 0;
555   }
556 
557   switch (render->valign) {
558     case GST_TEXT_RENDER_VALIGN_BOTTOM:
559       ypos = render->height - render->image_height - render->ypad;
560       break;
561     case GST_TEXT_RENDER_VALIGN_BASELINE:
562       ypos = render->height - (render->image_height + render->ypad);
563       break;
564     case GST_TEXT_RENDER_VALIGN_TOP:
565       ypos = render->ypad;
566       break;
567     default:
568       ypos = render->ypad;
569       break;
570   }
571 
572   if (render->text_image) {
573     if (render->use_ARGB) {
574       gst_text_renderer_image_to_argb (render, data, xpos, ypos,
575           render->width * 4);
576     } else {
577       gst_text_renderer_image_to_ayuv (render, data, xpos, ypos,
578           render->width * 4);
579     }
580   }
581   gst_buffer_unmap (outbuf, &map);
582 
583   ret = gst_pad_push (render->srcpad, outbuf);
584 
585 done:
586   if (caps)
587     gst_caps_unref (caps);
588   gst_buffer_unref (inbuf);
589 
590   return ret;
591 }
592 
593 static gboolean
gst_text_render_event(GstPad * pad,GstObject * parent,GstEvent * event)594 gst_text_render_event (GstPad * pad, GstObject * parent, GstEvent * event)
595 {
596   GstTextRender *render = GST_TEXT_RENDER (parent);
597   gboolean ret = TRUE;
598 
599   switch (GST_EVENT_TYPE (event)) {
600     case GST_EVENT_SEGMENT:
601     {
602       if (gst_pad_has_current_caps (render->srcpad)) {
603         ret = gst_pad_push_event (render->srcpad, event);
604       } else {
605         gst_event_replace (&render->segment_event, event);
606         gst_event_unref (event);
607       }
608       break;
609     }
610     default:
611       ret = gst_pad_push_event (render->srcpad, event);
612       break;
613   }
614 
615   return ret;
616 }
617 
618 static void
gst_text_render_finalize(GObject * object)619 gst_text_render_finalize (GObject * object)
620 {
621   GstTextRender *render = GST_TEXT_RENDER (object);
622 
623   gst_event_replace (&render->segment_event, NULL);
624 
625   g_free (render->text_image);
626 
627   if (render->layout)
628     g_object_unref (render->layout);
629 
630   if (render->pango_context)
631     g_object_unref (render->pango_context);
632 
633   G_OBJECT_CLASS (parent_class)->finalize (object);
634 }
635 
636 static void
gst_text_render_init(GstTextRender * render)637 gst_text_render_init (GstTextRender * render)
638 {
639   GstPadTemplate *template;
640   PangoFontMap *fontmap;
641 
642   /* sink */
643   template = gst_static_pad_template_get (&sink_template_factory);
644   render->sinkpad = gst_pad_new_from_template (template, "sink");
645   gst_object_unref (template);
646   gst_pad_set_chain_function (render->sinkpad,
647       GST_DEBUG_FUNCPTR (gst_text_render_chain));
648   gst_pad_set_event_function (render->sinkpad,
649       GST_DEBUG_FUNCPTR (gst_text_render_event));
650 
651   gst_element_add_pad (GST_ELEMENT (render), render->sinkpad);
652 
653   /* source */
654   template = gst_static_pad_template_get (&src_template_factory);
655   render->srcpad = gst_pad_new_from_template (template, "src");
656   gst_object_unref (template);
657 
658   gst_element_add_pad (GST_ELEMENT (render), render->srcpad);
659 
660   fontmap = pango_cairo_font_map_new ();
661   render->pango_context =
662       pango_font_map_create_context (PANGO_FONT_MAP (fontmap));
663   g_object_unref (fontmap);
664 
665   render->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
666   render->layout = pango_layout_new (render->pango_context);
667   pango_layout_set_alignment (render->layout,
668       (PangoAlignment) render->line_align);
669 
670   render->halign = DEFAULT_PROP_HALIGNMENT;
671   render->valign = DEFAULT_PROP_VALIGNMENT;
672   render->xpad = DEFAULT_PROP_XPAD;
673   render->ypad = DEFAULT_PROP_YPAD;
674 
675   render->width = DEFAULT_RENDER_WIDTH;
676   render->height = DEFAULT_RENDER_HEIGHT;
677 
678   render->use_ARGB = FALSE;
679 }
680 
681 static void
gst_text_render_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)682 gst_text_render_set_property (GObject * object, guint prop_id,
683     const GValue * value, GParamSpec * pspec)
684 {
685   GstTextRender *render = GST_TEXT_RENDER (object);
686 
687   switch (prop_id) {
688     case PROP_VALIGNMENT:
689       render->valign = g_value_get_enum (value);
690       break;
691     case PROP_HALIGNMENT:
692       render->halign = g_value_get_enum (value);
693       break;
694     case PROP_LINE_ALIGNMENT:
695       render->line_align = g_value_get_enum (value);
696       pango_layout_set_alignment (render->layout,
697           (PangoAlignment) render->line_align);
698       break;
699     case PROP_XPAD:
700       render->xpad = g_value_get_int (value);
701       break;
702     case PROP_YPAD:
703       render->ypad = g_value_get_int (value);
704       break;
705     case PROP_FONT_DESC:
706     {
707       PangoFontDescription *desc;
708 
709       desc = pango_font_description_from_string (g_value_get_string (value));
710       if (desc) {
711         GST_LOG ("font description set: %s", g_value_get_string (value));
712         GST_OBJECT_LOCK (render);
713         pango_layout_set_font_description (render->layout, desc);
714         gst_text_render_adjust_values_with_fontdesc (render, desc);
715         pango_font_description_free (desc);
716         gst_text_render_render_pangocairo (render);
717         GST_OBJECT_UNLOCK (render);
718       } else {
719         GST_WARNING ("font description parse failed: %s",
720             g_value_get_string (value));
721       }
722       break;
723     }
724 
725     default:
726       break;
727   }
728 }
729 
730 static void
gst_text_render_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)731 gst_text_render_get_property (GObject * object, guint prop_id,
732     GValue * value, GParamSpec * pspec)
733 {
734   GstTextRender *render = GST_TEXT_RENDER (object);
735 
736   switch (prop_id) {
737     case PROP_VALIGNMENT:
738       g_value_set_enum (value, render->valign);
739       break;
740     case PROP_HALIGNMENT:
741       g_value_set_enum (value, render->halign);
742       break;
743     case PROP_LINE_ALIGNMENT:
744       g_value_set_enum (value, render->line_align);
745       break;
746     case PROP_XPAD:
747       g_value_set_int (value, render->xpad);
748       break;
749     case PROP_YPAD:
750       g_value_set_int (value, render->ypad);
751       break;
752     default:
753       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
754       break;
755   }
756 }
757