pango: remove deprecated valign and halign properties
[platform/upstream/gstreamer.git] / ext / pango / gstbasetextoverlay.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
4  * Copyright (C) <2006> Julien Moutte <julien@moutte.net>
5  * Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com>
6  * Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net>
7  * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /**
26  * SECTION:element-textoverlay
27  * @see_also: #GstTextRender, #GstClockOverlay, #GstTimeOverlay, #GstSubParse
28  *
29  * This plugin renders text on top of a video stream. This can be either
30  * static text or text from buffers received on the text sink pad, e.g.
31  * as produced by the subparse element. If the text sink pad is not linked,
32  * the text set via the "text" property will be rendered. If the text sink
33  * pad is linked, text will be rendered as it is received on that pad,
34  * honouring and matching the buffer timestamps of both input streams.
35  *
36  * The text can contain newline characters and text wrapping is enabled by
37  * default.
38  *
39  * <refsect2>
40  * <title>Example launch lines</title>
41  * |[
42  * gst-launch -v videotestsrc ! textoverlay text="Room A" valign=top halign=left ! xvimagesink
43  * ]| Here is a simple pipeline that displays a static text in the top left
44  * corner of the video picture
45  * |[
46  * gst-launch -v filesrc location=subtitles.srt ! subparse ! txt.   videotestsrc ! timeoverlay ! textoverlay name=txt shaded-background=yes ! xvimagesink
47  * ]| Here is another pipeline that displays subtitles from an .srt subtitle
48  * file, centered at the bottom of the picture and with a rectangular shading
49  * around the text in the background:
50  * <para>
51  * If you do not have such a subtitle file, create one looking like this
52  * in a text editor:
53  * |[
54  * 1
55  * 00:00:03,000 --> 00:00:05,000
56  * Hello? (3-5s)
57  *
58  * 2
59  * 00:00:08,000 --> 00:00:13,000
60  * Yes, this is a subtitle. Don&apos;t
61  * you like it? (8-13s)
62  *
63  * 3
64  * 00:00:18,826 --> 00:01:02,886
65  * Uh? What are you talking about?
66  * I don&apos;t understand  (18-62s)
67  * ]|
68  * </para>
69  * </refsect2>
70  */
71
72 /* FIXME: alloc segment as part of instance struct */
73
74 #ifdef HAVE_CONFIG_H
75 #include <config.h>
76 #endif
77
78 #include <gst/video/video.h>
79
80 #include "gstbasetextoverlay.h"
81 #include "gsttextoverlay.h"
82 #include "gsttimeoverlay.h"
83 #include "gstclockoverlay.h"
84 #include "gsttextrender.h"
85 #include <string.h>
86
87 /* FIXME:
88  *  - use proper strides and offset for I420
89  *  - if text is wider than the video picture, it does not get
90  *    clipped properly during blitting (if wrapping is disabled)
91  *  - make 'shading_value' a property (or enum:  light/normal/dark/verydark)?
92  */
93
94 GST_DEBUG_CATEGORY (pango_debug);
95 #define GST_CAT_DEFAULT pango_debug
96
97 #define DEFAULT_PROP_TEXT       ""
98 #define DEFAULT_PROP_SHADING    FALSE
99 #define DEFAULT_PROP_VALIGNMENT GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE
100 #define DEFAULT_PROP_HALIGNMENT GST_BASE_TEXT_OVERLAY_HALIGN_CENTER
101 #define DEFAULT_PROP_XPAD       25
102 #define DEFAULT_PROP_YPAD       25
103 #define DEFAULT_PROP_DELTAX     0
104 #define DEFAULT_PROP_DELTAY     0
105 #define DEFAULT_PROP_XPOS       0.5
106 #define DEFAULT_PROP_YPOS       0.5
107 #define DEFAULT_PROP_WRAP_MODE  GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR
108 #define DEFAULT_PROP_FONT_DESC  ""
109 #define DEFAULT_PROP_SILENT     FALSE
110 #define DEFAULT_PROP_LINE_ALIGNMENT GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER
111 #define DEFAULT_PROP_WAIT_TEXT  TRUE
112 #define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE
113 #define DEFAULT_PROP_VERTICAL_RENDER  FALSE
114 #define DEFAULT_PROP_COLOR      0xffffffff
115 #define DEFAULT_PROP_OUTLINE_COLOR 0xff000000
116
117 /* make a property of me */
118 #define DEFAULT_SHADING_VALUE    -80
119
120 #define MINIMUM_OUTLINE_OFFSET 1.0
121 #define DEFAULT_SCALE_BASIS    640
122
123 #define COMP_Y(ret, r, g, b) \
124 { \
125    ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \
126    ret = CLAMP (ret, 0, 255); \
127 }
128
129 #define COMP_U(ret, r, g, b) \
130 { \
131    ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \
132    ret = CLAMP (ret, 0, 255); \
133 }
134
135 #define COMP_V(ret, r, g, b) \
136 { \
137    ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \
138    ret = CLAMP (ret, 0, 255); \
139 }
140
141 #define BLEND(ret, alpha, v0, v1) \
142 { \
143         ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \
144 }
145
146 #define OVER(ret, alphaA, Ca, alphaB, Cb, alphaNew)     \
147 { \
148     gint _tmp; \
149     _tmp = (Ca * alphaA + Cb * alphaB * (255 - alphaA) / 255) / alphaNew; \
150     ret = CLAMP (_tmp, 0, 255); \
151 }
152
153 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
154 # define CAIRO_ARGB_A 3
155 # define CAIRO_ARGB_R 2
156 # define CAIRO_ARGB_G 1
157 # define CAIRO_ARGB_B 0
158 #else
159 # define CAIRO_ARGB_A 0
160 # define CAIRO_ARGB_R 1
161 # define CAIRO_ARGB_G 2
162 # define CAIRO_ARGB_B 3
163 #endif
164
165 enum
166 {
167   PROP_0,
168   PROP_TEXT,
169   PROP_SHADING,
170   PROP_HALIGNMENT,
171   PROP_VALIGNMENT,
172   PROP_XPAD,
173   PROP_YPAD,
174   PROP_DELTAX,
175   PROP_DELTAY,
176   PROP_XPOS,
177   PROP_YPOS,
178   PROP_WRAP_MODE,
179   PROP_FONT_DESC,
180   PROP_SILENT,
181   PROP_LINE_ALIGNMENT,
182   PROP_WAIT_TEXT,
183   PROP_AUTO_ADJUST_SIZE,
184   PROP_VERTICAL_RENDER,
185   PROP_COLOR,
186   PROP_SHADOW,
187   PROP_OUTLINE_COLOR,
188   PROP_LAST
189 };
190
191 #define VIDEO_FORMATS "{ BGRx, RGBx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, \
192     I420, YV12, AYUV, YUY2, UYVY, v308, v210, v216, Y41B, Y42B, Y444, \
193     Y800, Y16, NV12, NV21, UYVP, A420, YUV9, IYU1 }"
194
195 static GstStaticPadTemplate src_template_factory =
196 GST_STATIC_PAD_TEMPLATE ("src",
197     GST_PAD_SRC,
198     GST_PAD_ALWAYS,
199     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
200     );
201
202 static GstStaticPadTemplate video_sink_template_factory =
203 GST_STATIC_PAD_TEMPLATE ("video_sink",
204     GST_PAD_SINK,
205     GST_PAD_ALWAYS,
206     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
207     );
208
209 #define GST_TYPE_BASE_TEXT_OVERLAY_VALIGN (gst_base_text_overlay_valign_get_type())
210 static GType
211 gst_base_text_overlay_valign_get_type (void)
212 {
213   static GType base_text_overlay_valign_type = 0;
214   static const GEnumValue base_text_overlay_valign[] = {
215     {GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"},
216     {GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"},
217     {GST_BASE_TEXT_OVERLAY_VALIGN_TOP, "top", "top"},
218     {GST_BASE_TEXT_OVERLAY_VALIGN_POS, "position", "position"},
219     {GST_BASE_TEXT_OVERLAY_VALIGN_CENTER, "center", "center"},
220     {0, NULL, NULL},
221   };
222
223   if (!base_text_overlay_valign_type) {
224     base_text_overlay_valign_type =
225         g_enum_register_static ("GstBaseTextOverlayVAlign",
226         base_text_overlay_valign);
227   }
228   return base_text_overlay_valign_type;
229 }
230
231 #define GST_TYPE_BASE_TEXT_OVERLAY_HALIGN (gst_base_text_overlay_halign_get_type())
232 static GType
233 gst_base_text_overlay_halign_get_type (void)
234 {
235   static GType base_text_overlay_halign_type = 0;
236   static const GEnumValue base_text_overlay_halign[] = {
237     {GST_BASE_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"},
238     {GST_BASE_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"},
239     {GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"},
240     {GST_BASE_TEXT_OVERLAY_HALIGN_POS, "position", "position"},
241     {0, NULL, NULL},
242   };
243
244   if (!base_text_overlay_halign_type) {
245     base_text_overlay_halign_type =
246         g_enum_register_static ("GstBaseTextOverlayHAlign",
247         base_text_overlay_halign);
248   }
249   return base_text_overlay_halign_type;
250 }
251
252
253 #define GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE (gst_base_text_overlay_wrap_mode_get_type())
254 static GType
255 gst_base_text_overlay_wrap_mode_get_type (void)
256 {
257   static GType base_text_overlay_wrap_mode_type = 0;
258   static const GEnumValue base_text_overlay_wrap_mode[] = {
259     {GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"},
260     {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"},
261     {GST_BASE_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"},
262     {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"},
263     {0, NULL, NULL},
264   };
265
266   if (!base_text_overlay_wrap_mode_type) {
267     base_text_overlay_wrap_mode_type =
268         g_enum_register_static ("GstBaseTextOverlayWrapMode",
269         base_text_overlay_wrap_mode);
270   }
271   return base_text_overlay_wrap_mode_type;
272 }
273
274 #define GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN (gst_base_text_overlay_line_align_get_type())
275 static GType
276 gst_base_text_overlay_line_align_get_type (void)
277 {
278   static GType base_text_overlay_line_align_type = 0;
279   static const GEnumValue base_text_overlay_line_align[] = {
280     {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"},
281     {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"},
282     {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"},
283     {0, NULL, NULL}
284   };
285
286   if (!base_text_overlay_line_align_type) {
287     base_text_overlay_line_align_type =
288         g_enum_register_static ("GstBaseTextOverlayLineAlign",
289         base_text_overlay_line_align);
290   }
291   return base_text_overlay_line_align_type;
292 }
293
294 #define GST_BASE_TEXT_OVERLAY_GET_LOCK(ov) (&GST_BASE_TEXT_OVERLAY (ov)->lock)
295 #define GST_BASE_TEXT_OVERLAY_GET_COND(ov) (&GST_BASE_TEXT_OVERLAY (ov)->cond)
296 #define GST_BASE_TEXT_OVERLAY_LOCK(ov)     (g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_LOCK (ov)))
297 #define GST_BASE_TEXT_OVERLAY_UNLOCK(ov)   (g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_LOCK (ov)))
298 #define GST_BASE_TEXT_OVERLAY_WAIT(ov)     (g_cond_wait (GST_BASE_TEXT_OVERLAY_GET_COND (ov), GST_BASE_TEXT_OVERLAY_GET_LOCK (ov)))
299 #define GST_BASE_TEXT_OVERLAY_SIGNAL(ov)   (g_cond_signal (GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
300 #define GST_BASE_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
301
302 static GstElementClass *parent_class = NULL;
303 static void gst_base_text_overlay_base_init (gpointer g_class);
304 static void gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass);
305 static void gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
306     GstBaseTextOverlayClass * klass);
307
308 static GstStateChangeReturn gst_base_text_overlay_change_state (GstElement *
309     element, GstStateChange transition);
310
311 static GstCaps *gst_base_text_overlay_getcaps (GstPad * pad,
312     GstBaseTextOverlay * overlay, GstCaps * filter);
313 static gboolean gst_base_text_overlay_setcaps (GstBaseTextOverlay * overlay,
314     GstCaps * caps);
315 static gboolean gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay,
316     GstCaps * caps);
317 static gboolean gst_base_text_overlay_src_event (GstPad * pad,
318     GstObject * parent, GstEvent * event);
319 static gboolean gst_base_text_overlay_src_query (GstPad * pad,
320     GstObject * parent, GstQuery * query);
321
322 static gboolean gst_base_text_overlay_video_event (GstPad * pad,
323     GstObject * parent, GstEvent * event);
324 static gboolean gst_base_text_overlay_video_query (GstPad * pad,
325     GstObject * parent, GstQuery * query);
326 static GstFlowReturn gst_base_text_overlay_video_chain (GstPad * pad,
327     GstObject * parent, GstBuffer * buffer);
328
329 static gboolean gst_base_text_overlay_text_event (GstPad * pad,
330     GstObject * parent, GstEvent * event);
331 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
332     GstObject * parent, GstBuffer * buffer);
333 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
334     GstPad * peer);
335 static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
336 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
337 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
338     overlay);
339
340 static void gst_base_text_overlay_finalize (GObject * object);
341 static void gst_base_text_overlay_set_property (GObject * object, guint prop_id,
342     const GValue * value, GParamSpec * pspec);
343 static void gst_base_text_overlay_get_property (GObject * object, guint prop_id,
344     GValue * value, GParamSpec * pspec);
345 static void
346 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
347     PangoFontDescription * desc);
348
349 GType
350 gst_base_text_overlay_get_type (void)
351 {
352   static GType type = 0;
353
354   if (g_once_init_enter ((gsize *) & type)) {
355     static const GTypeInfo info = {
356       sizeof (GstBaseTextOverlayClass),
357       (GBaseInitFunc) gst_base_text_overlay_base_init,
358       NULL,
359       (GClassInitFunc) gst_base_text_overlay_class_init,
360       NULL,
361       NULL,
362       sizeof (GstBaseTextOverlay),
363       0,
364       (GInstanceInitFunc) gst_base_text_overlay_init,
365     };
366
367     g_once_init_leave ((gsize *) & type,
368         g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTextOverlay", &info,
369             0));
370   }
371
372   return type;
373 }
374
375 static gchar *
376 gst_base_text_overlay_get_text (GstBaseTextOverlay * overlay,
377     GstBuffer * video_frame)
378 {
379   return g_strdup (overlay->default_text);
380 }
381
382 static void
383 gst_base_text_overlay_base_init (gpointer g_class)
384 {
385   GstBaseTextOverlayClass *klass = GST_BASE_TEXT_OVERLAY_CLASS (g_class);
386   PangoFontMap *fontmap;
387
388   /* Only lock for the subclasses here, the base class
389    * doesn't have this mutex yet and it's not necessary
390    * here */
391   if (klass->pango_lock)
392     g_mutex_lock (klass->pango_lock);
393   fontmap = pango_cairo_font_map_get_default ();
394   klass->pango_context =
395       pango_font_map_create_context (PANGO_FONT_MAP (fontmap));
396   if (klass->pango_lock)
397     g_mutex_unlock (klass->pango_lock);
398 }
399
400 static void
401 gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
402 {
403   GObjectClass *gobject_class;
404   GstElementClass *gstelement_class;
405
406   gobject_class = (GObjectClass *) klass;
407   gstelement_class = (GstElementClass *) klass;
408
409   parent_class = g_type_class_peek_parent (klass);
410
411   gobject_class->finalize = gst_base_text_overlay_finalize;
412   gobject_class->set_property = gst_base_text_overlay_set_property;
413   gobject_class->get_property = gst_base_text_overlay_get_property;
414
415   gst_element_class_add_pad_template (gstelement_class,
416       gst_static_pad_template_get (&src_template_factory));
417   gst_element_class_add_pad_template (gstelement_class,
418       gst_static_pad_template_get (&video_sink_template_factory));
419
420   gstelement_class->change_state =
421       GST_DEBUG_FUNCPTR (gst_base_text_overlay_change_state);
422
423   klass->pango_lock = g_slice_new (GMutex);
424   g_mutex_init (klass->pango_lock);
425
426   klass->get_text = gst_base_text_overlay_get_text;
427
428   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
429       g_param_spec_string ("text", "text",
430           "Text to be display.", DEFAULT_PROP_TEXT,
431           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
432   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
433       g_param_spec_boolean ("shaded-background", "shaded background",
434           "Whether to shade the background under the text area",
435           DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
436   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
437       g_param_spec_enum ("valignment", "vertical alignment",
438           "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
439           DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
440   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
441       g_param_spec_enum ("halignment", "horizontal alignment",
442           "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
443           DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
444   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
445       g_param_spec_int ("xpad", "horizontal paddding",
446           "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
447           DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
448   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
449       g_param_spec_int ("ypad", "vertical padding",
450           "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
451           DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
452   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
453       g_param_spec_int ("deltax", "X position modifier",
454           "Shift X position to the left or to the right. Unit is pixels.",
455           G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
456           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
457   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
458       g_param_spec_int ("deltay", "Y position modifier",
459           "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
460           DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
461   /**
462    * GstBaseTextOverlay:xpos
463    *
464    * Horizontal position of the rendered text when using positioned alignment.
465    *
466    * Since: 0.10.31
467    **/
468   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
469       g_param_spec_double ("xpos", "horizontal position",
470           "Horizontal position when using position alignment", 0, 1.0,
471           DEFAULT_PROP_XPOS,
472           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
473   /**
474    * GstBaseTextOverlay:ypos
475    *
476    * Vertical position of the rendered text when using positioned alignment.
477    *
478    * Since: 0.10.31
479    **/
480   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
481       g_param_spec_double ("ypos", "vertical position",
482           "Vertical position when using position alignment", 0, 1.0,
483           DEFAULT_PROP_YPOS,
484           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
485   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
486       g_param_spec_enum ("wrap-mode", "wrap mode",
487           "Whether to wrap the text and if so how.",
488           GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
489           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
490   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
491       g_param_spec_string ("font-desc", "font description",
492           "Pango font description of font to be used for rendering. "
493           "See documentation of pango_font_description_from_string "
494           "for syntax.", DEFAULT_PROP_FONT_DESC,
495           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
496   /**
497    * GstBaseTextOverlay:color
498    *
499    * Color of the rendered text.
500    *
501    * Since: 0.10.31
502    **/
503   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
504       g_param_spec_uint ("color", "Color",
505           "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
506           DEFAULT_PROP_COLOR,
507           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
508   /**
509    * GstTextOverlay:outline-color
510    *
511    * Color of the outline of the rendered text.
512    *
513    * Since: 0.10.35
514    **/
515   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
516       g_param_spec_uint ("outline-color", "Text Outline Color",
517           "Color to use for outline the text (big-endian ARGB).", 0,
518           G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
519           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
520
521   /**
522    * GstBaseTextOverlay:line-alignment
523    *
524    * Alignment of text lines relative to each other (for multi-line text)
525    *
526    * Since: 0.10.15
527    **/
528   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
529       g_param_spec_enum ("line-alignment", "line alignment",
530           "Alignment of text lines relative to each other.",
531           GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
532           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
533   /**
534    * GstBaseTextOverlay:silent
535    *
536    * If set, no text is rendered. Useful to switch off text rendering
537    * temporarily without removing the textoverlay element from the pipeline.
538    *
539    * Since: 0.10.15
540    **/
541   /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
542   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
543       g_param_spec_boolean ("silent", "silent",
544           "Whether to render the text string",
545           DEFAULT_PROP_SILENT,
546           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
547   /**
548    * GstBaseTextOverlay:wait-text
549    *
550    * If set, the video will block until a subtitle is received on the text pad.
551    * If video and subtitles are sent in sync, like from the same demuxer, this
552    * property should be set.
553    *
554    * Since: 0.10.20
555    **/
556   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
557       g_param_spec_boolean ("wait-text", "Wait Text",
558           "Whether to wait for subtitles",
559           DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
560
561   g_object_class_install_property (G_OBJECT_CLASS (klass),
562       PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
563           "Automatically adjust font size to screen-size.",
564           DEFAULT_PROP_AUTO_ADJUST_SIZE,
565           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
566
567   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
568       g_param_spec_boolean ("vertical-render", "vertical render",
569           "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
570           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
571 }
572
573 static void
574 gst_base_text_overlay_finalize (GObject * object)
575 {
576   GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
577
578   g_free (overlay->default_text);
579
580   if (overlay->composition) {
581     gst_video_overlay_composition_unref (overlay->composition);
582     overlay->composition = NULL;
583   }
584
585   if (overlay->text_image) {
586     gst_buffer_unref (overlay->text_image);
587     overlay->text_image = NULL;
588   }
589
590   if (overlay->layout) {
591     g_object_unref (overlay->layout);
592     overlay->layout = NULL;
593   }
594
595   if (overlay->text_buffer) {
596     gst_buffer_unref (overlay->text_buffer);
597     overlay->text_buffer = NULL;
598   }
599
600   g_mutex_clear (&overlay->lock);
601   g_cond_clear (&overlay->cond);
602
603   G_OBJECT_CLASS (parent_class)->finalize (object);
604 }
605
606 static void
607 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
608     GstBaseTextOverlayClass * klass)
609 {
610   GstPadTemplate *template;
611   PangoFontDescription *desc;
612
613   /* video sink */
614   template = gst_static_pad_template_get (&video_sink_template_factory);
615   overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
616   gst_object_unref (template);
617   gst_pad_set_event_function (overlay->video_sinkpad,
618       GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_event));
619   gst_pad_set_chain_function (overlay->video_sinkpad,
620       GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
621   gst_pad_set_query_function (overlay->video_sinkpad,
622       GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_query));
623   gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
624
625   template =
626       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
627       "text_sink");
628   if (template) {
629     /* text sink */
630     overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
631     gst_object_unref (template);
632
633     gst_pad_set_event_function (overlay->text_sinkpad,
634         GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
635     gst_pad_set_chain_function (overlay->text_sinkpad,
636         GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
637     gst_pad_set_link_function (overlay->text_sinkpad,
638         GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
639     gst_pad_set_unlink_function (overlay->text_sinkpad,
640         GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
641     gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
642   }
643
644   /* (video) source */
645   template = gst_static_pad_template_get (&src_template_factory);
646   overlay->srcpad = gst_pad_new_from_template (template, "src");
647   gst_object_unref (template);
648   gst_pad_set_event_function (overlay->srcpad,
649       GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_event));
650   gst_pad_set_query_function (overlay->srcpad,
651       GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_query));
652   gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
653
654   g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
655   overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
656   overlay->layout =
657       pango_layout_new (GST_BASE_TEXT_OVERLAY_GET_CLASS
658       (overlay)->pango_context);
659   desc =
660       pango_context_get_font_description (GST_BASE_TEXT_OVERLAY_GET_CLASS
661       (overlay)->pango_context);
662   gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
663
664   overlay->color = DEFAULT_PROP_COLOR;
665   overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
666   overlay->halign = DEFAULT_PROP_HALIGNMENT;
667   overlay->valign = DEFAULT_PROP_VALIGNMENT;
668   overlay->xpad = DEFAULT_PROP_XPAD;
669   overlay->ypad = DEFAULT_PROP_YPAD;
670   overlay->deltax = DEFAULT_PROP_DELTAX;
671   overlay->deltay = DEFAULT_PROP_DELTAY;
672   overlay->xpos = DEFAULT_PROP_XPOS;
673   overlay->ypos = DEFAULT_PROP_YPOS;
674
675   overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
676
677   overlay->want_shading = DEFAULT_PROP_SHADING;
678   overlay->shading_value = DEFAULT_SHADING_VALUE;
679   overlay->silent = DEFAULT_PROP_SILENT;
680   overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
681   overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
682
683   overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
684   overlay->need_render = TRUE;
685   overlay->text_image = NULL;
686   overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
687   gst_base_text_overlay_update_render_mode (overlay);
688
689   overlay->text_buffer = NULL;
690   overlay->text_linked = FALSE;
691   g_mutex_init (&overlay->lock);
692   g_cond_init (&overlay->cond);
693   gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
694   g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
695 }
696
697 static void
698 gst_base_text_overlay_update_wrap_mode (GstBaseTextOverlay * overlay)
699 {
700   if (overlay->wrap_mode == GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE) {
701     GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
702     pango_layout_set_width (overlay->layout, -1);
703   } else {
704     int width;
705
706     if (overlay->auto_adjust_size) {
707       width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
708       if (overlay->use_vertical_render) {
709         width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
710       }
711     } else {
712       width =
713           (overlay->use_vertical_render ? overlay->height : overlay->width) *
714           PANGO_SCALE;
715     }
716
717     GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
718     GST_DEBUG_OBJECT (overlay, "Set wrap mode    %d", overlay->wrap_mode);
719     pango_layout_set_width (overlay->layout, width);
720     pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
721   }
722 }
723
724 static void
725 gst_base_text_overlay_update_render_mode (GstBaseTextOverlay * overlay)
726 {
727   PangoMatrix matrix = PANGO_MATRIX_INIT;
728   PangoContext *context = pango_layout_get_context (overlay->layout);
729
730   if (overlay->use_vertical_render) {
731     pango_matrix_rotate (&matrix, -90);
732     pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
733     pango_context_set_matrix (context, &matrix);
734     pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
735   } else {
736     pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
737     pango_context_set_matrix (context, &matrix);
738     pango_layout_set_alignment (overlay->layout,
739         (PangoAlignment) overlay->line_align);
740   }
741 }
742
743 static gboolean
744 gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay, GstCaps * caps)
745 {
746   GstStructure *structure;
747
748   structure = gst_caps_get_structure (caps, 0);
749   overlay->have_pango_markup =
750       gst_structure_has_name (structure, "text/x-pango-markup");
751
752   return TRUE;
753 }
754
755 /* FIXME: upstream nego (e.g. when the video window is resized) */
756
757 /* only negotiate/query video overlay composition support for now */
758 static gboolean
759 gst_base_text_overlay_negotiate (GstBaseTextOverlay * overlay)
760 {
761   GstCaps *target;
762   GstQuery *query;
763   gboolean attach = FALSE;
764
765   GST_DEBUG_OBJECT (overlay, "performing negotiation");
766
767   target = gst_pad_get_current_caps (overlay->srcpad);
768
769   if (!target || gst_caps_is_empty (target))
770     goto no_format;
771
772   /* find supported meta */
773   query = gst_query_new_allocation (target, TRUE);
774
775   if (!gst_pad_peer_query (overlay->srcpad, query)) {
776     /* no problem, we use the query defaults */
777     GST_DEBUG_OBJECT (overlay, "ALLOCATION query failed");
778   }
779
780   if (gst_query_find_allocation_meta (query,
781           GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL))
782     attach = TRUE;
783
784   overlay->attach_compo_to_buffer = attach;
785
786   gst_query_unref (query);
787   gst_caps_unref (target);
788
789   return TRUE;
790
791 no_format:
792   {
793     if (target)
794       gst_caps_unref (target);
795     return FALSE;
796   }
797 }
798
799 static gboolean
800 gst_base_text_overlay_setcaps (GstBaseTextOverlay * overlay, GstCaps * caps)
801 {
802   GstVideoInfo info;
803   gboolean ret = FALSE;
804
805   if (!gst_video_info_from_caps (&info, caps))
806     goto invalid_caps;
807
808   overlay->info = info;
809   overlay->format = GST_VIDEO_INFO_FORMAT (&info);
810   overlay->width = GST_VIDEO_INFO_WIDTH (&info);
811   overlay->height = GST_VIDEO_INFO_HEIGHT (&info);
812
813   ret = gst_pad_set_caps (overlay->srcpad, caps);
814
815   if (ret) {
816     GST_BASE_TEXT_OVERLAY_LOCK (overlay);
817     g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
818     gst_base_text_overlay_negotiate (overlay);
819     gst_base_text_overlay_update_wrap_mode (overlay);
820     g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
821     GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
822   }
823
824   return ret;
825
826   /* ERRORS */
827 invalid_caps:
828   {
829     GST_DEBUG_OBJECT (overlay, "could not parse caps");
830     return FALSE;
831   }
832 }
833
834 static void
835 gst_base_text_overlay_set_property (GObject * object, guint prop_id,
836     const GValue * value, GParamSpec * pspec)
837 {
838   GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
839
840   GST_BASE_TEXT_OVERLAY_LOCK (overlay);
841   switch (prop_id) {
842     case PROP_TEXT:
843       g_free (overlay->default_text);
844       overlay->default_text = g_value_dup_string (value);
845       overlay->need_render = TRUE;
846       break;
847     case PROP_SHADING:
848       overlay->want_shading = g_value_get_boolean (value);
849       break;
850     case PROP_XPAD:
851       overlay->xpad = g_value_get_int (value);
852       break;
853     case PROP_YPAD:
854       overlay->ypad = g_value_get_int (value);
855       break;
856     case PROP_DELTAX:
857       overlay->deltax = g_value_get_int (value);
858       break;
859     case PROP_DELTAY:
860       overlay->deltay = g_value_get_int (value);
861       break;
862     case PROP_XPOS:
863       overlay->xpos = g_value_get_double (value);
864       break;
865     case PROP_YPOS:
866       overlay->ypos = g_value_get_double (value);
867       break;
868     case PROP_VALIGNMENT:
869       overlay->valign = g_value_get_enum (value);
870       break;
871     case PROP_HALIGNMENT:
872       overlay->halign = g_value_get_enum (value);
873       break;
874     case PROP_WRAP_MODE:
875       overlay->wrap_mode = g_value_get_enum (value);
876       g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
877       gst_base_text_overlay_update_wrap_mode (overlay);
878       g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
879       break;
880     case PROP_FONT_DESC:
881     {
882       PangoFontDescription *desc;
883       const gchar *fontdesc_str;
884
885       fontdesc_str = g_value_get_string (value);
886       g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
887       desc = pango_font_description_from_string (fontdesc_str);
888       if (desc) {
889         GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
890         pango_layout_set_font_description (overlay->layout, desc);
891         gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
892         pango_font_description_free (desc);
893       } else {
894         GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
895             fontdesc_str);
896       }
897       g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
898       break;
899     }
900     case PROP_COLOR:
901       overlay->color = g_value_get_uint (value);
902       break;
903     case PROP_OUTLINE_COLOR:
904       overlay->outline_color = g_value_get_uint (value);
905       break;
906     case PROP_SILENT:
907       overlay->silent = g_value_get_boolean (value);
908       break;
909     case PROP_LINE_ALIGNMENT:
910       overlay->line_align = g_value_get_enum (value);
911       g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
912       pango_layout_set_alignment (overlay->layout,
913           (PangoAlignment) overlay->line_align);
914       g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
915       break;
916     case PROP_WAIT_TEXT:
917       overlay->wait_text = g_value_get_boolean (value);
918       break;
919     case PROP_AUTO_ADJUST_SIZE:
920       overlay->auto_adjust_size = g_value_get_boolean (value);
921       overlay->need_render = TRUE;
922       break;
923     case PROP_VERTICAL_RENDER:
924       overlay->use_vertical_render = g_value_get_boolean (value);
925       g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
926       gst_base_text_overlay_update_render_mode (overlay);
927       g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
928       overlay->need_render = TRUE;
929       break;
930     default:
931       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
932       break;
933   }
934
935   overlay->need_render = TRUE;
936   GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
937 }
938
939 static void
940 gst_base_text_overlay_get_property (GObject * object, guint prop_id,
941     GValue * value, GParamSpec * pspec)
942 {
943   GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
944
945   GST_BASE_TEXT_OVERLAY_LOCK (overlay);
946   switch (prop_id) {
947     case PROP_TEXT:
948       g_value_set_string (value, overlay->default_text);
949       break;
950     case PROP_SHADING:
951       g_value_set_boolean (value, overlay->want_shading);
952       break;
953     case PROP_XPAD:
954       g_value_set_int (value, overlay->xpad);
955       break;
956     case PROP_YPAD:
957       g_value_set_int (value, overlay->ypad);
958       break;
959     case PROP_DELTAX:
960       g_value_set_int (value, overlay->deltax);
961       break;
962     case PROP_DELTAY:
963       g_value_set_int (value, overlay->deltay);
964       break;
965     case PROP_XPOS:
966       g_value_set_double (value, overlay->xpos);
967       break;
968     case PROP_YPOS:
969       g_value_set_double (value, overlay->ypos);
970       break;
971     case PROP_VALIGNMENT:
972       g_value_set_enum (value, overlay->valign);
973       break;
974     case PROP_HALIGNMENT:
975       g_value_set_enum (value, overlay->halign);
976       break;
977     case PROP_WRAP_MODE:
978       g_value_set_enum (value, overlay->wrap_mode);
979       break;
980     case PROP_SILENT:
981       g_value_set_boolean (value, overlay->silent);
982       break;
983     case PROP_LINE_ALIGNMENT:
984       g_value_set_enum (value, overlay->line_align);
985       break;
986     case PROP_WAIT_TEXT:
987       g_value_set_boolean (value, overlay->wait_text);
988       break;
989     case PROP_AUTO_ADJUST_SIZE:
990       g_value_set_boolean (value, overlay->auto_adjust_size);
991       break;
992     case PROP_VERTICAL_RENDER:
993       g_value_set_boolean (value, overlay->use_vertical_render);
994       break;
995     case PROP_COLOR:
996       g_value_set_uint (value, overlay->color);
997       break;
998     case PROP_OUTLINE_COLOR:
999       g_value_set_uint (value, overlay->outline_color);
1000       break;
1001     default:
1002       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1003       break;
1004   }
1005
1006   overlay->need_render = TRUE;
1007   GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1008 }
1009
1010 static gboolean
1011 gst_base_text_overlay_src_query (GstPad * pad, GstObject * parent,
1012     GstQuery * query)
1013 {
1014   gboolean ret = FALSE;
1015   GstBaseTextOverlay *overlay;
1016
1017   overlay = GST_BASE_TEXT_OVERLAY (parent);
1018
1019   switch (GST_QUERY_TYPE (query)) {
1020     case GST_QUERY_CAPS:
1021     {
1022       GstCaps *filter, *caps;
1023
1024       gst_query_parse_caps (query, &filter);
1025       caps = gst_base_text_overlay_getcaps (pad, overlay, filter);
1026       gst_query_set_caps_result (query, caps);
1027       gst_caps_unref (caps);
1028       ret = TRUE;
1029       break;
1030     }
1031     default:
1032       ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1033       break;
1034   }
1035
1036   return ret;
1037 }
1038
1039 static gboolean
1040 gst_base_text_overlay_src_event (GstPad * pad, GstObject * parent,
1041     GstEvent * event)
1042 {
1043   gboolean ret = FALSE;
1044   GstBaseTextOverlay *overlay = NULL;
1045
1046   overlay = GST_BASE_TEXT_OVERLAY (parent);
1047
1048   switch (GST_EVENT_TYPE (event)) {
1049     case GST_EVENT_SEEK:{
1050       GstSeekFlags flags;
1051
1052       /* We don't handle seek if we have not text pad */
1053       if (!overlay->text_linked) {
1054         GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1055         ret = gst_pad_push_event (overlay->video_sinkpad, event);
1056         goto beach;
1057       }
1058
1059       GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1060
1061       gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1062
1063       /* Flush downstream, only for flushing seek */
1064       if (flags & GST_SEEK_FLAG_FLUSH)
1065         gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1066
1067       /* Mark ourself as flushing, unblock chains */
1068       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1069       overlay->video_flushing = TRUE;
1070       overlay->text_flushing = TRUE;
1071       gst_base_text_overlay_pop_text (overlay);
1072       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1073
1074       /* Seek on each sink pad */
1075       gst_event_ref (event);
1076       ret = gst_pad_push_event (overlay->video_sinkpad, event);
1077       if (ret) {
1078         ret = gst_pad_push_event (overlay->text_sinkpad, event);
1079       } else {
1080         gst_event_unref (event);
1081       }
1082       break;
1083     }
1084     default:
1085       if (overlay->text_linked) {
1086         gst_event_ref (event);
1087         ret = gst_pad_push_event (overlay->video_sinkpad, event);
1088         gst_pad_push_event (overlay->text_sinkpad, event);
1089       } else {
1090         ret = gst_pad_push_event (overlay->video_sinkpad, event);
1091       }
1092       break;
1093   }
1094
1095 beach:
1096
1097   return ret;
1098 }
1099
1100 static GstCaps *
1101 gst_base_text_overlay_getcaps (GstPad * pad, GstBaseTextOverlay * overlay,
1102     GstCaps * filter)
1103 {
1104   GstPad *otherpad;
1105   GstCaps *caps;
1106
1107   if (G_UNLIKELY (!overlay))
1108     return gst_pad_get_pad_template_caps (pad);
1109
1110   if (pad == overlay->srcpad)
1111     otherpad = overlay->video_sinkpad;
1112   else
1113     otherpad = overlay->srcpad;
1114
1115   /* we can do what the peer can */
1116   caps = gst_pad_peer_query_caps (otherpad, filter);
1117   if (caps) {
1118     GstCaps *temp, *templ;
1119
1120     GST_DEBUG_OBJECT (pad, "peer caps  %" GST_PTR_FORMAT, caps);
1121
1122     /* filtered against our padtemplate */
1123     templ = gst_pad_get_pad_template_caps (otherpad);
1124     GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, templ);
1125     temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1126     GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1127     gst_caps_unref (caps);
1128     gst_caps_unref (templ);
1129     /* this is what we can do */
1130     caps = temp;
1131   } else {
1132     /* no peer, our padtemplate is enough then */
1133     caps = gst_pad_get_pad_template_caps (pad);
1134     if (filter) {
1135       GstCaps *intersection;
1136
1137       intersection =
1138           gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1139       gst_caps_unref (caps);
1140       caps = intersection;
1141     }
1142   }
1143
1144   GST_DEBUG_OBJECT (overlay, "returning  %" GST_PTR_FORMAT, caps);
1145
1146   return caps;
1147 }
1148
1149 static void
1150 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1151     PangoFontDescription * desc)
1152 {
1153   gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1154   overlay->shadow_offset = (double) (font_size) / 13.0;
1155   overlay->outline_offset = (double) (font_size) / 15.0;
1156   if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1157     overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1158 }
1159
1160 static void
1161 gst_base_text_overlay_get_pos (GstBaseTextOverlay * overlay,
1162     gint * xpos, gint * ypos)
1163 {
1164   gint width, height;
1165   GstBaseTextOverlayVAlign valign;
1166   GstBaseTextOverlayHAlign halign;
1167
1168   width = overlay->image_width;
1169   height = overlay->image_height;
1170
1171   if (overlay->use_vertical_render)
1172     halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1173   else
1174     halign = overlay->halign;
1175
1176   switch (halign) {
1177     case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1178       *xpos = overlay->xpad;
1179       break;
1180     case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1181       *xpos = (overlay->width - width) / 2;
1182       break;
1183     case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1184       *xpos = overlay->width - width - overlay->xpad;
1185       break;
1186     case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1187       *xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1188       *xpos = CLAMP (*xpos, 0, overlay->width - width);
1189       if (*xpos < 0)
1190         *xpos = 0;
1191       break;
1192     default:
1193       *xpos = 0;
1194   }
1195   *xpos += overlay->deltax;
1196
1197   if (overlay->use_vertical_render)
1198     valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1199   else
1200     valign = overlay->valign;
1201
1202   switch (valign) {
1203     case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1204       *ypos = overlay->height - height - overlay->ypad;
1205       break;
1206     case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1207       *ypos = overlay->height - (height + overlay->ypad);
1208       break;
1209     case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1210       *ypos = overlay->ypad;
1211       break;
1212     case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1213       *ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1214       *ypos = CLAMP (*ypos, 0, overlay->height - height);
1215       break;
1216     case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1217       *ypos = (overlay->height - height) / 2;
1218       break;
1219     default:
1220       *ypos = overlay->ypad;
1221       break;
1222   }
1223   *ypos += overlay->deltay;
1224 }
1225
1226 static inline void
1227 gst_base_text_overlay_set_composition (GstBaseTextOverlay * overlay)
1228 {
1229   gint xpos, ypos;
1230   GstVideoOverlayRectangle *rectangle;
1231
1232   gst_base_text_overlay_get_pos (overlay, &xpos, &ypos);
1233
1234   if (overlay->text_image) {
1235     rectangle = gst_video_overlay_rectangle_new_argb (overlay->text_image,
1236         overlay->image_width, overlay->image_height, 4 * overlay->image_width,
1237         xpos, ypos, overlay->image_width, overlay->image_height,
1238         GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA);
1239
1240     if (overlay->composition)
1241       gst_video_overlay_composition_unref (overlay->composition);
1242     overlay->composition = gst_video_overlay_composition_new (rectangle);
1243     gst_video_overlay_rectangle_unref (rectangle);
1244
1245   } else if (overlay->composition) {
1246     gst_video_overlay_composition_unref (overlay->composition);
1247     overlay->composition = NULL;
1248   }
1249 }
1250
1251 static void
1252 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1253     const gchar * string, gint textlen)
1254 {
1255   cairo_t *cr;
1256   cairo_surface_t *surface;
1257   PangoRectangle ink_rect, logical_rect;
1258   cairo_matrix_t cairo_matrix;
1259   int width, height;
1260   double scalef = 1.0;
1261   double a, r, g, b;
1262   GstBuffer *buffer;
1263   GstMapInfo map;
1264
1265   g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1266
1267   if (overlay->auto_adjust_size) {
1268     /* 640 pixel is default */
1269     scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1270   }
1271   pango_layout_set_width (overlay->layout, -1);
1272   /* set text on pango layout */
1273   pango_layout_set_markup (overlay->layout, string, textlen);
1274
1275   /* get subtitle image size */
1276   pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1277
1278   width = (logical_rect.width + overlay->shadow_offset) * scalef;
1279
1280   if (width + overlay->deltax >
1281       (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1282     /*
1283      * subtitle image width is larger then overlay width
1284      * so rearrange overlay wrap mode.
1285      */
1286     gst_base_text_overlay_update_wrap_mode (overlay);
1287     pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1288     width = overlay->width;
1289   }
1290
1291   height =
1292       (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1293   if (height > overlay->height) {
1294     height = overlay->height;
1295   }
1296   if (overlay->use_vertical_render) {
1297     PangoRectangle rect;
1298     PangoContext *context;
1299     PangoMatrix matrix = PANGO_MATRIX_INIT;
1300     int tmp;
1301
1302     context = pango_layout_get_context (overlay->layout);
1303
1304     pango_matrix_rotate (&matrix, -90);
1305
1306     rect.x = rect.y = 0;
1307     rect.width = width;
1308     rect.height = height;
1309     pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1310     matrix.x0 = -rect.x;
1311     matrix.y0 = -rect.y;
1312
1313     pango_context_set_matrix (context, &matrix);
1314
1315     cairo_matrix.xx = matrix.xx;
1316     cairo_matrix.yx = matrix.yx;
1317     cairo_matrix.xy = matrix.xy;
1318     cairo_matrix.yy = matrix.yy;
1319     cairo_matrix.x0 = matrix.x0;
1320     cairo_matrix.y0 = matrix.y0;
1321     cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1322
1323     tmp = height;
1324     height = width;
1325     width = tmp;
1326   } else {
1327     cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1328   }
1329
1330   /* reallocate overlay buffer */
1331   buffer = gst_buffer_new_and_alloc (4 * width * height);
1332   gst_buffer_replace (&overlay->text_image, buffer);
1333   gst_buffer_unref (buffer);
1334
1335   gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
1336   surface = cairo_image_surface_create_for_data (map.data,
1337       CAIRO_FORMAT_ARGB32, width, height, width * 4);
1338   cr = cairo_create (surface);
1339
1340   /* clear surface */
1341   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1342   cairo_paint (cr);
1343
1344   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1345
1346   if (overlay->want_shading)
1347     cairo_paint_with_alpha (cr, overlay->shading_value);
1348
1349   /* apply transformations */
1350   cairo_set_matrix (cr, &cairo_matrix);
1351
1352   /* FIXME: We use show_layout everywhere except for the surface
1353    * because it's really faster and internally does all kinds of
1354    * caching. Unfortunately we have to paint to a cairo path for
1355    * the outline and this is slow. Once Pango supports user fonts
1356    * we should use them, see
1357    * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1358    *
1359    * Idea would the be, to create a cairo user font that
1360    * does shadow, outline, text painting in the
1361    * render_glyph function.
1362    */
1363
1364   /* draw shadow text */
1365   cairo_save (cr);
1366   cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1367   cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1368   pango_cairo_show_layout (cr, overlay->layout);
1369   cairo_restore (cr);
1370
1371   a = (overlay->outline_color >> 24) & 0xff;
1372   r = (overlay->outline_color >> 16) & 0xff;
1373   g = (overlay->outline_color >> 8) & 0xff;
1374   b = (overlay->outline_color >> 0) & 0xff;
1375
1376   /* draw outline text */
1377   cairo_save (cr);
1378   cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1379   cairo_set_line_width (cr, overlay->outline_offset);
1380   pango_cairo_layout_path (cr, overlay->layout);
1381   cairo_stroke (cr);
1382   cairo_restore (cr);
1383
1384   a = (overlay->color >> 24) & 0xff;
1385   r = (overlay->color >> 16) & 0xff;
1386   g = (overlay->color >> 8) & 0xff;
1387   b = (overlay->color >> 0) & 0xff;
1388
1389   /* draw text */
1390   cairo_save (cr);
1391   cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1392   pango_cairo_show_layout (cr, overlay->layout);
1393   cairo_restore (cr);
1394
1395   cairo_destroy (cr);
1396   cairo_surface_destroy (surface);
1397   gst_buffer_unmap (buffer, &map);
1398   overlay->image_width = width;
1399   overlay->image_height = height;
1400   overlay->baseline_y = ink_rect.y;
1401   g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1402
1403   gst_base_text_overlay_set_composition (overlay);
1404 }
1405
1406 #define BOX_XPAD         6
1407 #define BOX_YPAD         6
1408
1409 static inline void
1410 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1411     GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1412 {
1413   gint i, j, dest_stride;
1414   guint8 *dest_ptr;
1415
1416   dest_stride = dest->info.stride[0];
1417   dest_ptr = dest->data[0];
1418
1419   x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1420   x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1421
1422   y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1423   y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1424
1425   for (i = y0; i < y1; ++i) {
1426     for (j = x0; j < x1; ++j) {
1427       gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
1428
1429       dest_ptr[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1430     }
1431   }
1432 }
1433
1434 static inline void
1435 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1436     GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1437 {
1438   gint i, j;
1439   guint dest_stride, pixel_stride;
1440   guint8 *dest_ptr;
1441
1442   dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
1443   dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
1444   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
1445
1446   x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1447   x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1448
1449   y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1450   y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1451
1452   if (x0 != 0)
1453     x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
1454   if (x1 != 0)
1455     x1 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x1);
1456
1457   if (y0 != 0)
1458     y0 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y0);
1459   if (y1 != 0)
1460     y1 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y1);
1461
1462   for (i = y0; i < y1; i++) {
1463     for (j = x0; j < x1; j++) {
1464       gint y;
1465       gint y_pos;
1466
1467       y_pos = (i * dest_stride) + j * pixel_stride;
1468       y = dest_ptr[y_pos] + overlay->shading_value;
1469
1470       dest_ptr[y_pos] = CLAMP (y, 0, 255);
1471     }
1472   }
1473 }
1474
1475 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1476 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1477 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1478 static inline void
1479 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
1480     GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1481 {
1482   gint i, j;
1483   guint8 *dest_ptr;
1484
1485   dest_ptr = dest->data[0];
1486
1487   x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1488   x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1489
1490   y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1491   y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1492
1493   for (i = y0; i < y1; i++) {
1494     for (j = x0; j < x1; j++) {
1495       gint y, y_pos, k;
1496
1497       y_pos = (i * 4 * overlay->width) + j * 4;
1498       for (k = 0; k < 4; k++) {
1499         y = dest_ptr[y_pos + k] + overlay->shading_value;
1500         dest_ptr[y_pos + k] = CLAMP (y, 0, 255);
1501       }
1502     }
1503   }
1504 }
1505
1506 #define ARGB_SHADE_FUNCTION(name, OFFSET)       \
1507 static inline void \
1508 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
1509 gint x0, gint x1, gint y0, gint y1) \
1510 { \
1511   gint i, j;\
1512   guint8 *dest_ptr;\
1513   \
1514   dest_ptr = dest->data[0];\
1515   \
1516   x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1517   x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1518   \
1519   y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1520   y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1521   \
1522   for (i = y0; i < y1; i++) {\
1523     for (j = x0; j < x1; j++) {\
1524       gint y, y_pos, k;\
1525       y_pos = (i * 4 * overlay->width) + j * 4;\
1526       for (k = OFFSET; k < 3+OFFSET; k++) {\
1527         y = dest_ptr[y_pos + k] + overlay->shading_value;\
1528         dest_ptr[y_pos + k] = CLAMP (y, 0, 255);\
1529       }\
1530     }\
1531   }\
1532 }
1533 ARGB_SHADE_FUNCTION (ARGB, 1);
1534 ARGB_SHADE_FUNCTION (ABGR, 1);
1535 ARGB_SHADE_FUNCTION (RGBA, 0);
1536 ARGB_SHADE_FUNCTION (BGRA, 0);
1537
1538 static void
1539 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1540     const gchar * text, gint textlen)
1541 {
1542   gchar *string;
1543
1544   if (!overlay->need_render) {
1545     GST_DEBUG ("Using previously rendered text.");
1546     return;
1547   }
1548
1549   /* -1 is the whole string */
1550   if (text != NULL && textlen < 0) {
1551     textlen = strlen (text);
1552   }
1553
1554   if (text != NULL) {
1555     string = g_strndup (text, textlen);
1556   } else {                      /* empty string */
1557     string = g_strdup (" ");
1558   }
1559   g_strdelimit (string, "\r\t", ' ');
1560   textlen = strlen (string);
1561
1562   /* FIXME: should we check for UTF-8 here? */
1563
1564   GST_DEBUG ("Rendering '%s'", string);
1565   gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1566
1567   g_free (string);
1568
1569   overlay->need_render = FALSE;
1570 }
1571
1572 static GstFlowReturn
1573 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1574     GstBuffer * video_frame)
1575 {
1576   gint xpos, ypos;
1577   GstVideoFrame frame;
1578
1579   if (overlay->composition == NULL)
1580     goto done;
1581
1582   if (gst_pad_check_reconfigure (overlay->srcpad))
1583     gst_base_text_overlay_negotiate (overlay);
1584
1585   video_frame = gst_buffer_make_writable (video_frame);
1586
1587   if (overlay->attach_compo_to_buffer) {
1588     GST_DEBUG_OBJECT (overlay, "Attaching text overlay image to video buffer");
1589     gst_buffer_add_video_overlay_composition_meta (video_frame,
1590         overlay->composition);
1591     /* FIXME: emulate shaded background box if want_shading=true */
1592     goto done;
1593   }
1594
1595   if (!gst_video_frame_map (&frame, &overlay->info, video_frame,
1596           GST_MAP_READWRITE))
1597     goto invalid_frame;
1598
1599   gst_base_text_overlay_get_pos (overlay, &xpos, &ypos);
1600
1601   /* shaded background box */
1602   if (overlay->want_shading) {
1603     switch (overlay->format) {
1604       case GST_VIDEO_FORMAT_I420:
1605       case GST_VIDEO_FORMAT_NV12:
1606       case GST_VIDEO_FORMAT_NV21:
1607         gst_base_text_overlay_shade_planar_Y (overlay, &frame,
1608             xpos, xpos + overlay->image_width,
1609             ypos, ypos + overlay->image_height);
1610         break;
1611       case GST_VIDEO_FORMAT_AYUV:
1612       case GST_VIDEO_FORMAT_UYVY:
1613         gst_base_text_overlay_shade_packed_Y (overlay, &frame,
1614             xpos, xpos + overlay->image_width,
1615             ypos, ypos + overlay->image_height);
1616         break;
1617       case GST_VIDEO_FORMAT_xRGB:
1618         gst_base_text_overlay_shade_xRGB (overlay, &frame,
1619             xpos, xpos + overlay->image_width,
1620             ypos, ypos + overlay->image_height);
1621         break;
1622       case GST_VIDEO_FORMAT_xBGR:
1623         gst_base_text_overlay_shade_xBGR (overlay, &frame,
1624             xpos, xpos + overlay->image_width,
1625             ypos, ypos + overlay->image_height);
1626         break;
1627       case GST_VIDEO_FORMAT_BGRx:
1628         gst_base_text_overlay_shade_BGRx (overlay, &frame,
1629             xpos, xpos + overlay->image_width,
1630             ypos, ypos + overlay->image_height);
1631         break;
1632       case GST_VIDEO_FORMAT_RGBx:
1633         gst_base_text_overlay_shade_RGBx (overlay, &frame,
1634             xpos, xpos + overlay->image_width,
1635             ypos, ypos + overlay->image_height);
1636         break;
1637       case GST_VIDEO_FORMAT_ARGB:
1638         gst_base_text_overlay_shade_ARGB (overlay, &frame,
1639             xpos, xpos + overlay->image_width,
1640             ypos, ypos + overlay->image_height);
1641         break;
1642       case GST_VIDEO_FORMAT_ABGR:
1643         gst_base_text_overlay_shade_ABGR (overlay, &frame,
1644             xpos, xpos + overlay->image_width,
1645             ypos, ypos + overlay->image_height);
1646         break;
1647       case GST_VIDEO_FORMAT_RGBA:
1648         gst_base_text_overlay_shade_RGBA (overlay, &frame,
1649             xpos, xpos + overlay->image_width,
1650             ypos, ypos + overlay->image_height);
1651         break;
1652       case GST_VIDEO_FORMAT_BGRA:
1653         gst_base_text_overlay_shade_BGRA (overlay, &frame,
1654             xpos, xpos + overlay->image_width,
1655             ypos, ypos + overlay->image_height);
1656         break;
1657       default:
1658         g_assert_not_reached ();
1659     }
1660   }
1661
1662   gst_video_overlay_composition_blend (overlay->composition, &frame);
1663
1664   gst_video_frame_unmap (&frame);
1665
1666 done:
1667
1668   return gst_pad_push (overlay->srcpad, video_frame);
1669
1670   /* ERRORS */
1671 invalid_frame:
1672   {
1673     gst_buffer_unref (video_frame);
1674     GST_DEBUG_OBJECT (overlay, "received invalid buffer");
1675     return GST_FLOW_OK;
1676   }
1677 }
1678
1679 static GstPadLinkReturn
1680 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
1681 {
1682   GstBaseTextOverlay *overlay;
1683
1684   overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1685   if (G_UNLIKELY (!overlay))
1686     return GST_PAD_LINK_REFUSED;
1687
1688   GST_DEBUG_OBJECT (overlay, "Text pad linked");
1689
1690   overlay->text_linked = TRUE;
1691
1692   gst_object_unref (overlay);
1693
1694   return GST_PAD_LINK_OK;
1695 }
1696
1697 static void
1698 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
1699 {
1700   GstBaseTextOverlay *overlay;
1701
1702   /* don't use gst_pad_get_parent() here, will deadlock */
1703   overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
1704
1705   GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
1706
1707   overlay->text_linked = FALSE;
1708
1709   gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
1710 }
1711
1712 static gboolean
1713 gst_base_text_overlay_text_event (GstPad * pad, GstObject * parent,
1714     GstEvent * event)
1715 {
1716   gboolean ret = FALSE;
1717   GstBaseTextOverlay *overlay = NULL;
1718
1719   overlay = GST_BASE_TEXT_OVERLAY (parent);
1720
1721   GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
1722
1723   switch (GST_EVENT_TYPE (event)) {
1724     case GST_EVENT_CAPS:
1725     {
1726       GstCaps *caps;
1727
1728       gst_event_parse_caps (event, &caps);
1729       ret = gst_base_text_overlay_setcaps_txt (overlay, caps);
1730       gst_event_unref (event);
1731       break;
1732     }
1733     case GST_EVENT_SEGMENT:
1734     {
1735       const GstSegment *segment;
1736
1737       overlay->text_eos = FALSE;
1738
1739       gst_event_parse_segment (event, &segment);
1740
1741       if (segment->format == GST_FORMAT_TIME) {
1742         GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1743         gst_segment_copy_into (segment, &overlay->text_segment);
1744         GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
1745             &overlay->text_segment);
1746         GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1747       } else {
1748         GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
1749             ("received non-TIME newsegment event on text input"));
1750       }
1751
1752       gst_event_unref (event);
1753       ret = TRUE;
1754
1755       /* wake up the video chain, it might be waiting for a text buffer or
1756        * a text segment update */
1757       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1758       GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
1759       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1760       break;
1761     }
1762     case GST_EVENT_FLUSH_STOP:
1763       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1764       GST_INFO_OBJECT (overlay, "text flush stop");
1765       overlay->text_flushing = FALSE;
1766       overlay->text_eos = FALSE;
1767       gst_base_text_overlay_pop_text (overlay);
1768       gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
1769       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1770       gst_event_unref (event);
1771       ret = TRUE;
1772       break;
1773     case GST_EVENT_FLUSH_START:
1774       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1775       GST_INFO_OBJECT (overlay, "text flush start");
1776       overlay->text_flushing = TRUE;
1777       GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
1778       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1779       gst_event_unref (event);
1780       ret = TRUE;
1781       break;
1782     case GST_EVENT_EOS:
1783       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1784       overlay->text_eos = TRUE;
1785       GST_INFO_OBJECT (overlay, "text EOS");
1786       /* wake up the video chain, it might be waiting for a text buffer or
1787        * a text segment update */
1788       GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
1789       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1790       gst_event_unref (event);
1791       ret = TRUE;
1792       break;
1793     default:
1794       ret = gst_pad_event_default (pad, parent, event);
1795       break;
1796   }
1797
1798   return ret;
1799 }
1800
1801 static gboolean
1802 gst_base_text_overlay_video_event (GstPad * pad, GstObject * parent,
1803     GstEvent * event)
1804 {
1805   gboolean ret = FALSE;
1806   GstBaseTextOverlay *overlay = NULL;
1807
1808   overlay = GST_BASE_TEXT_OVERLAY (parent);
1809
1810   GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
1811
1812   switch (GST_EVENT_TYPE (event)) {
1813     case GST_EVENT_CAPS:
1814     {
1815       GstCaps *caps;
1816
1817       gst_event_parse_caps (event, &caps);
1818       ret = gst_base_text_overlay_setcaps (overlay, caps);
1819       gst_event_unref (event);
1820       break;
1821     }
1822     case GST_EVENT_SEGMENT:
1823     {
1824       const GstSegment *segment;
1825
1826       GST_DEBUG_OBJECT (overlay, "received new segment");
1827
1828       gst_event_parse_segment (event, &segment);
1829
1830       if (segment->format == GST_FORMAT_TIME) {
1831         GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
1832             &overlay->segment);
1833
1834         gst_segment_copy_into (segment, &overlay->segment);
1835       } else {
1836         GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
1837             ("received non-TIME newsegment event on video input"));
1838       }
1839
1840       ret = gst_pad_event_default (pad, parent, event);
1841       break;
1842     }
1843     case GST_EVENT_EOS:
1844       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1845       GST_INFO_OBJECT (overlay, "video EOS");
1846       overlay->video_eos = TRUE;
1847       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1848       ret = gst_pad_event_default (pad, parent, event);
1849       break;
1850     case GST_EVENT_FLUSH_START:
1851       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1852       GST_INFO_OBJECT (overlay, "video flush start");
1853       overlay->video_flushing = TRUE;
1854       GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
1855       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1856       ret = gst_pad_event_default (pad, parent, event);
1857       break;
1858     case GST_EVENT_FLUSH_STOP:
1859       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1860       GST_INFO_OBJECT (overlay, "video flush stop");
1861       overlay->video_flushing = FALSE;
1862       overlay->video_eos = FALSE;
1863       gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
1864       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1865       ret = gst_pad_event_default (pad, parent, event);
1866       break;
1867     default:
1868       ret = gst_pad_event_default (pad, parent, event);
1869       break;
1870   }
1871
1872   return ret;
1873 }
1874
1875 static gboolean
1876 gst_base_text_overlay_video_query (GstPad * pad, GstObject * parent,
1877     GstQuery * query)
1878 {
1879   gboolean ret = FALSE;
1880   GstBaseTextOverlay *overlay;
1881
1882   overlay = GST_BASE_TEXT_OVERLAY (parent);
1883
1884   switch (GST_QUERY_TYPE (query)) {
1885     case GST_QUERY_CAPS:
1886     {
1887       GstCaps *filter, *caps;
1888
1889       gst_query_parse_caps (query, &filter);
1890       caps = gst_base_text_overlay_getcaps (pad, overlay, filter);
1891       gst_query_set_caps_result (query, caps);
1892       gst_caps_unref (caps);
1893       ret = TRUE;
1894       break;
1895     }
1896     default:
1897       ret = gst_pad_query_default (pad, parent, query);
1898       break;
1899   }
1900
1901   return ret;
1902 }
1903
1904 /* Called with lock held */
1905 static void
1906 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
1907 {
1908   g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
1909
1910   if (overlay->text_buffer) {
1911     GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
1912         overlay->text_buffer);
1913     gst_buffer_unref (overlay->text_buffer);
1914     overlay->text_buffer = NULL;
1915   }
1916
1917   /* Let the text task know we used that buffer */
1918   GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
1919 }
1920
1921 /* We receive text buffers here. If they are out of segment we just ignore them.
1922    If the buffer is in our segment we keep it internally except if another one
1923    is already waiting here, in that case we wait that it gets kicked out */
1924 static GstFlowReturn
1925 gst_base_text_overlay_text_chain (GstPad * pad, GstObject * parent,
1926     GstBuffer * buffer)
1927 {
1928   GstFlowReturn ret = GST_FLOW_OK;
1929   GstBaseTextOverlay *overlay = NULL;
1930   gboolean in_seg = FALSE;
1931   guint64 clip_start = 0, clip_stop = 0;
1932
1933   overlay = GST_BASE_TEXT_OVERLAY (parent);
1934
1935   GST_BASE_TEXT_OVERLAY_LOCK (overlay);
1936
1937   if (overlay->text_flushing) {
1938     GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1939     ret = GST_FLOW_FLUSHING;
1940     GST_LOG_OBJECT (overlay, "text flushing");
1941     goto beach;
1942   }
1943
1944   if (overlay->text_eos) {
1945     GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1946     ret = GST_FLOW_EOS;
1947     GST_LOG_OBJECT (overlay, "text EOS");
1948     goto beach;
1949   }
1950
1951   GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT "  BUFFER: ts=%"
1952       GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
1953       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
1954       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
1955           GST_BUFFER_DURATION (buffer)));
1956
1957   if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
1958     GstClockTime stop;
1959
1960     if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
1961       stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
1962     else
1963       stop = GST_CLOCK_TIME_NONE;
1964
1965     in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
1966         GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
1967   } else {
1968     in_seg = TRUE;
1969   }
1970
1971   if (in_seg) {
1972     if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
1973       GST_BUFFER_TIMESTAMP (buffer) = clip_start;
1974     else if (GST_BUFFER_DURATION_IS_VALID (buffer))
1975       GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
1976
1977     /* Wait for the previous buffer to go away */
1978     while (overlay->text_buffer != NULL) {
1979       GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
1980           GST_DEBUG_PAD_NAME (pad));
1981       GST_BASE_TEXT_OVERLAY_WAIT (overlay);
1982       GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
1983       if (overlay->text_flushing) {
1984         GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
1985         ret = GST_FLOW_FLUSHING;
1986         goto beach;
1987       }
1988     }
1989
1990     if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
1991       overlay->text_segment.position = clip_start;
1992
1993     overlay->text_buffer = buffer;
1994     /* That's a new text buffer we need to render */
1995     overlay->need_render = TRUE;
1996
1997     /* in case the video chain is waiting for a text buffer, wake it up */
1998     GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
1999   }
2000
2001   GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2002
2003 beach:
2004
2005   return ret;
2006 }
2007
2008 static GstFlowReturn
2009 gst_base_text_overlay_video_chain (GstPad * pad, GstObject * parent,
2010     GstBuffer * buffer)
2011 {
2012   GstBaseTextOverlayClass *klass;
2013   GstBaseTextOverlay *overlay;
2014   GstFlowReturn ret = GST_FLOW_OK;
2015   gboolean in_seg = FALSE;
2016   guint64 start, stop, clip_start = 0, clip_stop = 0;
2017   gchar *text = NULL;
2018
2019   overlay = GST_BASE_TEXT_OVERLAY (parent);
2020   klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2021
2022   if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2023     goto missing_timestamp;
2024
2025   /* ignore buffers that are outside of the current segment */
2026   start = GST_BUFFER_TIMESTAMP (buffer);
2027
2028   if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2029     stop = GST_CLOCK_TIME_NONE;
2030   } else {
2031     stop = start + GST_BUFFER_DURATION (buffer);
2032   }
2033
2034   GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT "  BUFFER: ts=%"
2035       GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2036       GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2037
2038   /* segment_clip() will adjust start unconditionally to segment_start if
2039    * no stop time is provided, so handle this ourselves */
2040   if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2041     goto out_of_segment;
2042
2043   in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2044       &clip_start, &clip_stop);
2045
2046   if (!in_seg)
2047     goto out_of_segment;
2048
2049   /* if the buffer is only partially in the segment, fix up stamps */
2050   if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2051     GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2052     buffer = gst_buffer_make_writable (buffer);
2053     GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2054     if (stop != -1)
2055       GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2056   }
2057
2058   /* now, after we've done the clipping, fix up end time if there's no
2059    * duration (we only use those estimated values internally though, we
2060    * don't want to set bogus values on the buffer itself) */
2061   if (stop == -1) {
2062     GstCaps *caps;
2063     GstStructure *s;
2064     gint fps_num, fps_denom;
2065
2066     /* FIXME, store this in setcaps */
2067     caps = gst_pad_get_current_caps (pad);
2068     s = gst_caps_get_structure (caps, 0);
2069     if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2070         fps_num && fps_denom) {
2071       GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2072       stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2073     } else {
2074       GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2075       stop = start + 1;         /* we need to assume some interval */
2076     }
2077     gst_caps_unref (caps);
2078   }
2079
2080   gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2081
2082 wait_for_text_buf:
2083
2084   GST_BASE_TEXT_OVERLAY_LOCK (overlay);
2085
2086   if (overlay->video_flushing)
2087     goto flushing;
2088
2089   if (overlay->video_eos)
2090     goto have_eos;
2091
2092   if (overlay->silent) {
2093     GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2094     ret = gst_pad_push (overlay->srcpad, buffer);
2095
2096     /* Update position */
2097     overlay->segment.position = clip_start;
2098
2099     return ret;
2100   }
2101
2102   /* Text pad not linked, rendering internal text */
2103   if (!overlay->text_linked) {
2104     if (klass->get_text) {
2105       text = klass->get_text (overlay, buffer);
2106     } else {
2107       text = g_strdup (overlay->default_text);
2108     }
2109
2110     GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2111         "text: '%s'", GST_STR_NULL (text));
2112
2113     GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2114
2115     if (text != NULL && *text != '\0') {
2116       /* Render and push */
2117       gst_base_text_overlay_render_text (overlay, text, -1);
2118       ret = gst_base_text_overlay_push_frame (overlay, buffer);
2119     } else {
2120       /* Invalid or empty string */
2121       ret = gst_pad_push (overlay->srcpad, buffer);
2122     }
2123   } else {
2124     /* Text pad linked, check if we have a text buffer queued */
2125     if (overlay->text_buffer) {
2126       gboolean pop_text = FALSE, valid_text_time = TRUE;
2127       GstClockTime text_start = GST_CLOCK_TIME_NONE;
2128       GstClockTime text_end = GST_CLOCK_TIME_NONE;
2129       GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2130       GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2131       GstClockTime vid_running_time, vid_running_time_end;
2132
2133       /* if the text buffer isn't stamped right, pop it off the
2134        * queue and display it for the current video frame only */
2135       if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2136           !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2137         GST_WARNING_OBJECT (overlay,
2138             "Got text buffer with invalid timestamp or duration");
2139         pop_text = TRUE;
2140         valid_text_time = FALSE;
2141       } else {
2142         text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2143         text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2144       }
2145
2146       vid_running_time =
2147           gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2148           start);
2149       vid_running_time_end =
2150           gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2151           stop);
2152
2153       /* If timestamp and duration are valid */
2154       if (valid_text_time) {
2155         text_running_time =
2156             gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2157             text_start);
2158         text_running_time_end =
2159             gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2160             text_end);
2161       }
2162
2163       GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2164           GST_TIME_ARGS (text_running_time),
2165           GST_TIME_ARGS (text_running_time_end));
2166       GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2167           GST_TIME_ARGS (vid_running_time),
2168           GST_TIME_ARGS (vid_running_time_end));
2169
2170       /* Text too old or in the future */
2171       if (valid_text_time && text_running_time_end <= vid_running_time) {
2172         /* text buffer too old, get rid of it and do nothing  */
2173         GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2174         pop_text = FALSE;
2175         gst_base_text_overlay_pop_text (overlay);
2176         GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2177         goto wait_for_text_buf;
2178       } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2179         GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2180         GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2181         /* Push the video frame */
2182         ret = gst_pad_push (overlay->srcpad, buffer);
2183       } else {
2184         GstMapInfo map;
2185         gchar *in_text;
2186         gsize in_size;
2187
2188         gst_buffer_map (overlay->text_buffer, &map, GST_MAP_READ);
2189         in_text = (gchar *) map.data;
2190         in_size = map.size;
2191
2192         /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2193          * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2194          * here on purpose, this is something that needs fixing upstream */
2195         if (!g_utf8_validate (in_text, in_size, NULL)) {
2196           const gchar *end = NULL;
2197
2198           GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2199           in_text = g_strndup (in_text, in_size);
2200           while (!g_utf8_validate (in_text, in_size, &end) && end)
2201             *((gchar *) end) = '*';
2202         }
2203
2204         /* Get the string */
2205         if (overlay->have_pango_markup) {
2206           text = g_strndup (in_text, in_size);
2207         } else {
2208           text = g_markup_escape_text (in_text, in_size);
2209         }
2210
2211         if (text != NULL && *text != '\0') {
2212           gint text_len = strlen (text);
2213
2214           while (text_len > 0 && (text[text_len - 1] == '\n' ||
2215                   text[text_len - 1] == '\r')) {
2216             --text_len;
2217           }
2218           GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2219           gst_base_text_overlay_render_text (overlay, text, text_len);
2220         } else {
2221           GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2222           gst_base_text_overlay_render_text (overlay, " ", 1);
2223         }
2224         if (in_text != (gchar *) map.data)
2225           g_free (in_text);
2226
2227         gst_buffer_unmap (overlay->text_buffer, &map);
2228
2229         GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2230         ret = gst_base_text_overlay_push_frame (overlay, buffer);
2231
2232         if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2233           GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2234           pop_text = TRUE;
2235         }
2236       }
2237       if (pop_text) {
2238         GST_BASE_TEXT_OVERLAY_LOCK (overlay);
2239         gst_base_text_overlay_pop_text (overlay);
2240         GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2241       }
2242     } else {
2243       gboolean wait_for_text_buf = TRUE;
2244
2245       if (overlay->text_eos)
2246         wait_for_text_buf = FALSE;
2247
2248       if (!overlay->wait_text)
2249         wait_for_text_buf = FALSE;
2250
2251       /* Text pad linked, but no text buffer available - what now? */
2252       if (overlay->text_segment.format == GST_FORMAT_TIME) {
2253         GstClockTime text_start_running_time, text_position_running_time;
2254         GstClockTime vid_running_time;
2255
2256         vid_running_time =
2257             gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2258             GST_BUFFER_TIMESTAMP (buffer));
2259         text_start_running_time =
2260             gst_segment_to_running_time (&overlay->text_segment,
2261             GST_FORMAT_TIME, overlay->text_segment.start);
2262         text_position_running_time =
2263             gst_segment_to_running_time (&overlay->text_segment,
2264             GST_FORMAT_TIME, overlay->text_segment.position);
2265
2266         if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2267                 vid_running_time < text_start_running_time) ||
2268             (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2269                 vid_running_time < text_position_running_time)) {
2270           wait_for_text_buf = FALSE;
2271         }
2272       }
2273
2274       if (wait_for_text_buf) {
2275         GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2276         GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2277         GST_DEBUG_OBJECT (overlay, "resuming");
2278         GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2279         goto wait_for_text_buf;
2280       } else {
2281         GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2282         GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2283         ret = gst_pad_push (overlay->srcpad, buffer);
2284       }
2285     }
2286   }
2287
2288   g_free (text);
2289
2290   /* Update position */
2291   overlay->segment.position = clip_start;
2292
2293   return ret;
2294
2295 missing_timestamp:
2296   {
2297     GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2298     gst_buffer_unref (buffer);
2299     return GST_FLOW_OK;
2300   }
2301
2302 flushing:
2303   {
2304     GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2305     GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2306     gst_buffer_unref (buffer);
2307     return GST_FLOW_FLUSHING;
2308   }
2309 have_eos:
2310   {
2311     GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2312     GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2313     gst_buffer_unref (buffer);
2314     return GST_FLOW_EOS;
2315   }
2316 out_of_segment:
2317   {
2318     GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2319     gst_buffer_unref (buffer);
2320     return GST_FLOW_OK;
2321   }
2322 }
2323
2324 static GstStateChangeReturn
2325 gst_base_text_overlay_change_state (GstElement * element,
2326     GstStateChange transition)
2327 {
2328   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2329   GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2330
2331   switch (transition) {
2332     case GST_STATE_CHANGE_PAUSED_TO_READY:
2333       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
2334       overlay->text_flushing = TRUE;
2335       overlay->video_flushing = TRUE;
2336       /* pop_text will broadcast on the GCond and thus also make the video
2337        * chain exit if it's waiting for a text buffer */
2338       gst_base_text_overlay_pop_text (overlay);
2339       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2340       break;
2341     default:
2342       break;
2343   }
2344
2345   ret = parent_class->change_state (element, transition);
2346   if (ret == GST_STATE_CHANGE_FAILURE)
2347     return ret;
2348
2349   switch (transition) {
2350     case GST_STATE_CHANGE_READY_TO_PAUSED:
2351       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
2352       overlay->text_flushing = FALSE;
2353       overlay->video_flushing = FALSE;
2354       overlay->video_eos = FALSE;
2355       overlay->text_eos = FALSE;
2356       gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2357       gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2358       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
2359       break;
2360     default:
2361       break;
2362   }
2363
2364   return ret;
2365 }
2366
2367 static gboolean
2368 plugin_init (GstPlugin * plugin)
2369 {
2370   if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2371           GST_TYPE_TEXT_OVERLAY) ||
2372       !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2373           GST_TYPE_TIME_OVERLAY) ||
2374       !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2375           GST_TYPE_CLOCK_OVERLAY) ||
2376       !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2377           GST_TYPE_TEXT_RENDER)) {
2378     return FALSE;
2379   }
2380
2381   /*texttestsrc_plugin_init(module, plugin); */
2382
2383   GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2384
2385   return TRUE;
2386 }
2387
2388 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2389     pango, "Pango-based text rendering and overlay", plugin_init,
2390     VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)