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>
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.
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.
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.
26 * SECTION:element-textoverlay
27 * @see_also: #GstTextRender, #GstClockOverlay, #GstTimeOverlay, #GstSubParse
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.
36 * The text can contain newline characters and text wrapping is enabled by
40 * <title>Example launch lines</title>
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
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:
51 * If you do not have such a subtitle file, create one looking like this
55 * 00:00:03,000 --> 00:00:05,000
59 * 00:00:08,000 --> 00:00:13,000
60 * Yes, this is a subtitle. Don't
61 * you like it? (8-13s)
64 * 00:00:18,826 --> 00:01:02,886
65 * Uh? What are you talking about?
66 * I don't understand (18-62s)
72 /* FIXME: alloc segment as part of instance struct */
78 #include <gst/video/video.h>
80 #include "gstbasetextoverlay.h"
81 #include "gsttextoverlay.h"
82 #include "gsttimeoverlay.h"
83 #include "gstclockoverlay.h"
84 #include "gsttextrender.h"
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)?
94 GST_DEBUG_CATEGORY (pango_debug);
95 #define GST_CAT_DEFAULT pango_debug
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_VALIGN "baseline"
102 #define DEFAULT_PROP_HALIGN "center"
103 #define DEFAULT_PROP_XPAD 25
104 #define DEFAULT_PROP_YPAD 25
105 #define DEFAULT_PROP_DELTAX 0
106 #define DEFAULT_PROP_DELTAY 0
107 #define DEFAULT_PROP_XPOS 0.5
108 #define DEFAULT_PROP_YPOS 0.5
109 #define DEFAULT_PROP_WRAP_MODE GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR
110 #define DEFAULT_PROP_FONT_DESC ""
111 #define DEFAULT_PROP_SILENT FALSE
112 #define DEFAULT_PROP_LINE_ALIGNMENT GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER
113 #define DEFAULT_PROP_WAIT_TEXT TRUE
114 #define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE
115 #define DEFAULT_PROP_VERTICAL_RENDER FALSE
116 #define DEFAULT_PROP_COLOR 0xffffffff
117 #define DEFAULT_PROP_OUTLINE_COLOR 0xff000000
119 /* make a property of me */
120 #define DEFAULT_SHADING_VALUE -80
122 #define MINIMUM_OUTLINE_OFFSET 1.0
123 #define DEFAULT_SCALE_BASIS 640
125 #define COMP_Y(ret, r, g, b) \
127 ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \
128 ret = CLAMP (ret, 0, 255); \
131 #define COMP_U(ret, r, g, b) \
133 ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \
134 ret = CLAMP (ret, 0, 255); \
137 #define COMP_V(ret, r, g, b) \
139 ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \
140 ret = CLAMP (ret, 0, 255); \
143 #define BLEND(ret, alpha, v0, v1) \
145 ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \
148 #define OVER(ret, alphaA, Ca, alphaB, Cb, alphaNew) \
151 _tmp = (Ca * alphaA + Cb * alphaB * (255 - alphaA) / 255) / alphaNew; \
152 ret = CLAMP (_tmp, 0, 255); \
155 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
156 # define CAIRO_ARGB_A 3
157 # define CAIRO_ARGB_R 2
158 # define CAIRO_ARGB_G 1
159 # define CAIRO_ARGB_B 0
161 # define CAIRO_ARGB_A 0
162 # define CAIRO_ARGB_R 1
163 # define CAIRO_ARGB_G 2
164 # define CAIRO_ARGB_B 3
172 PROP_VALIGN, /* deprecated */
173 PROP_HALIGN, /* deprecated */
187 PROP_AUTO_ADJUST_SIZE,
188 PROP_VERTICAL_RENDER,
195 #define VIDEO_FORMATS "{ BGRx, RGBx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, AYUV, I420, UYVY, NV12, NV21 } "
197 static GstStaticPadTemplate src_template_factory =
198 GST_STATIC_PAD_TEMPLATE ("src",
201 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
204 static GstStaticPadTemplate video_sink_template_factory =
205 GST_STATIC_PAD_TEMPLATE ("video_sink",
208 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
211 #define GST_TYPE_BASE_TEXT_OVERLAY_VALIGN (gst_base_text_overlay_valign_get_type())
213 gst_base_text_overlay_valign_get_type (void)
215 static GType base_text_overlay_valign_type = 0;
216 static const GEnumValue base_text_overlay_valign[] = {
217 {GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"},
218 {GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"},
219 {GST_BASE_TEXT_OVERLAY_VALIGN_TOP, "top", "top"},
220 {GST_BASE_TEXT_OVERLAY_VALIGN_POS, "position", "position"},
221 {GST_BASE_TEXT_OVERLAY_VALIGN_CENTER, "center", "center"},
225 if (!base_text_overlay_valign_type) {
226 base_text_overlay_valign_type =
227 g_enum_register_static ("GstBaseTextOverlayVAlign",
228 base_text_overlay_valign);
230 return base_text_overlay_valign_type;
233 #define GST_TYPE_BASE_TEXT_OVERLAY_HALIGN (gst_base_text_overlay_halign_get_type())
235 gst_base_text_overlay_halign_get_type (void)
237 static GType base_text_overlay_halign_type = 0;
238 static const GEnumValue base_text_overlay_halign[] = {
239 {GST_BASE_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"},
240 {GST_BASE_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"},
241 {GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"},
242 {GST_BASE_TEXT_OVERLAY_HALIGN_POS, "position", "position"},
246 if (!base_text_overlay_halign_type) {
247 base_text_overlay_halign_type =
248 g_enum_register_static ("GstBaseTextOverlayHAlign",
249 base_text_overlay_halign);
251 return base_text_overlay_halign_type;
255 #define GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE (gst_base_text_overlay_wrap_mode_get_type())
257 gst_base_text_overlay_wrap_mode_get_type (void)
259 static GType base_text_overlay_wrap_mode_type = 0;
260 static const GEnumValue base_text_overlay_wrap_mode[] = {
261 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"},
262 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"},
263 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"},
264 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"},
268 if (!base_text_overlay_wrap_mode_type) {
269 base_text_overlay_wrap_mode_type =
270 g_enum_register_static ("GstBaseTextOverlayWrapMode",
271 base_text_overlay_wrap_mode);
273 return base_text_overlay_wrap_mode_type;
276 #define GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN (gst_base_text_overlay_line_align_get_type())
278 gst_base_text_overlay_line_align_get_type (void)
280 static GType base_text_overlay_line_align_type = 0;
281 static const GEnumValue base_text_overlay_line_align[] = {
282 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"},
283 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"},
284 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"},
288 if (!base_text_overlay_line_align_type) {
289 base_text_overlay_line_align_type =
290 g_enum_register_static ("GstBaseTextOverlayLineAlign",
291 base_text_overlay_line_align);
293 return base_text_overlay_line_align_type;
296 #define GST_BASE_TEXT_OVERLAY_GET_COND(ov) (((GstBaseTextOverlay *)ov)->cond)
297 #define GST_BASE_TEXT_OVERLAY_WAIT(ov) (g_cond_wait (GST_BASE_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov)))
298 #define GST_BASE_TEXT_OVERLAY_SIGNAL(ov) (g_cond_signal (GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
299 #define GST_BASE_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
301 static GstElementClass *parent_class = NULL;
302 static void gst_base_text_overlay_base_init (gpointer g_class);
303 static void gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass);
304 static void gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
305 GstBaseTextOverlayClass * klass);
307 static GstStateChangeReturn gst_base_text_overlay_change_state (GstElement *
308 element, GstStateChange transition);
310 static GstCaps *gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter);
311 static gboolean gst_base_text_overlay_setcaps (GstPad * pad, GstCaps * caps);
312 static gboolean gst_base_text_overlay_setcaps_txt (GstPad * pad,
314 static gboolean gst_base_text_overlay_src_event (GstPad * pad,
316 static gboolean gst_base_text_overlay_src_query (GstPad * pad,
319 static gboolean gst_base_text_overlay_video_event (GstPad * pad,
321 static GstFlowReturn gst_base_text_overlay_video_chain (GstPad * pad,
324 static gboolean gst_base_text_overlay_text_event (GstPad * pad,
326 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
328 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
330 static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
331 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
332 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
335 static void gst_base_text_overlay_finalize (GObject * object);
336 static void gst_base_text_overlay_set_property (GObject * object, guint prop_id,
337 const GValue * value, GParamSpec * pspec);
338 static void gst_base_text_overlay_get_property (GObject * object, guint prop_id,
339 GValue * value, GParamSpec * pspec);
341 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
342 PangoFontDescription * desc);
345 gst_base_text_overlay_get_type (void)
347 static GType type = 0;
349 if (g_once_init_enter ((gsize *) & type)) {
350 static const GTypeInfo info = {
351 sizeof (GstBaseTextOverlayClass),
352 (GBaseInitFunc) gst_base_text_overlay_base_init,
354 (GClassInitFunc) gst_base_text_overlay_class_init,
357 sizeof (GstBaseTextOverlay),
359 (GInstanceInitFunc) gst_base_text_overlay_init,
362 g_once_init_leave ((gsize *) & type,
363 g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTextOverlay", &info,
371 gst_base_text_overlay_get_text (GstBaseTextOverlay * overlay,
372 GstBuffer * video_frame)
374 return g_strdup (overlay->default_text);
378 gst_base_text_overlay_base_init (gpointer g_class)
380 GstBaseTextOverlayClass *klass = GST_BASE_TEXT_OVERLAY_CLASS (g_class);
381 PangoFontMap *fontmap;
383 /* Only lock for the subclasses here, the base class
384 * doesn't have this mutex yet and it's not necessary
386 if (klass->pango_lock)
387 g_mutex_lock (klass->pango_lock);
388 fontmap = pango_cairo_font_map_get_default ();
389 klass->pango_context =
390 pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
391 if (klass->pango_lock)
392 g_mutex_unlock (klass->pango_lock);
396 gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
398 GObjectClass *gobject_class;
399 GstElementClass *gstelement_class;
401 gobject_class = (GObjectClass *) klass;
402 gstelement_class = (GstElementClass *) klass;
404 parent_class = g_type_class_peek_parent (klass);
406 gobject_class->finalize = gst_base_text_overlay_finalize;
407 gobject_class->set_property = gst_base_text_overlay_set_property;
408 gobject_class->get_property = gst_base_text_overlay_get_property;
410 gst_element_class_add_pad_template (gstelement_class,
411 gst_static_pad_template_get (&src_template_factory));
412 gst_element_class_add_pad_template (gstelement_class,
413 gst_static_pad_template_get (&video_sink_template_factory));
415 gstelement_class->change_state =
416 GST_DEBUG_FUNCPTR (gst_base_text_overlay_change_state);
418 klass->pango_lock = g_mutex_new ();
420 klass->get_text = gst_base_text_overlay_get_text;
422 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
423 g_param_spec_string ("text", "text",
424 "Text to be display.", DEFAULT_PROP_TEXT,
425 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
426 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
427 g_param_spec_boolean ("shaded-background", "shaded background",
428 "Whether to shade the background under the text area",
429 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
430 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
431 g_param_spec_enum ("valignment", "vertical alignment",
432 "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
433 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
434 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
435 g_param_spec_enum ("halignment", "horizontal alignment",
436 "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
437 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
438 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
439 g_param_spec_string ("valign", "vertical alignment",
440 "Vertical alignment of the text (deprecated; use valignment)",
441 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
442 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
443 g_param_spec_string ("halign", "horizontal alignment",
444 "Horizontal alignment of the text (deprecated; use halignment)",
445 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
446 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
447 g_param_spec_int ("xpad", "horizontal paddding",
448 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
449 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
450 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
451 g_param_spec_int ("ypad", "vertical padding",
452 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
453 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
454 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
455 g_param_spec_int ("deltax", "X position modifier",
456 "Shift X position to the left or to the right. Unit is pixels.",
457 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
458 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
459 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
460 g_param_spec_int ("deltay", "Y position modifier",
461 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
462 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
464 * GstBaseTextOverlay:xpos
466 * Horizontal position of the rendered text when using positioned alignment.
470 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
471 g_param_spec_double ("xpos", "horizontal position",
472 "Horizontal position when using position alignment", 0, 1.0,
474 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
476 * GstBaseTextOverlay:ypos
478 * Vertical position of the rendered text when using positioned alignment.
482 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
483 g_param_spec_double ("ypos", "vertical position",
484 "Vertical position when using position alignment", 0, 1.0,
486 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
487 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
488 g_param_spec_enum ("wrap-mode", "wrap mode",
489 "Whether to wrap the text and if so how.",
490 GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
491 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
492 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
493 g_param_spec_string ("font-desc", "font description",
494 "Pango font description of font to be used for rendering. "
495 "See documentation of pango_font_description_from_string "
496 "for syntax.", DEFAULT_PROP_FONT_DESC,
497 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
499 * GstBaseTextOverlay:color
501 * Color of the rendered text.
505 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
506 g_param_spec_uint ("color", "Color",
507 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
509 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
511 * GstTextOverlay:outline-color
513 * Color of the outline of the rendered text.
517 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
518 g_param_spec_uint ("outline-color", "Text Outline Color",
519 "Color to use for outline the text (big-endian ARGB).", 0,
520 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
521 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
524 * GstBaseTextOverlay:line-alignment
526 * Alignment of text lines relative to each other (for multi-line text)
530 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
531 g_param_spec_enum ("line-alignment", "line alignment",
532 "Alignment of text lines relative to each other.",
533 GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
534 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
536 * GstBaseTextOverlay:silent
538 * If set, no text is rendered. Useful to switch off text rendering
539 * temporarily without removing the textoverlay element from the pipeline.
543 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
544 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
545 g_param_spec_boolean ("silent", "silent",
546 "Whether to render the text string",
548 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
550 * GstBaseTextOverlay:wait-text
552 * If set, the video will block until a subtitle is received on the text pad.
553 * If video and subtitles are sent in sync, like from the same demuxer, this
554 * property should be set.
558 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
559 g_param_spec_boolean ("wait-text", "Wait Text",
560 "Whether to wait for subtitles",
561 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
563 g_object_class_install_property (G_OBJECT_CLASS (klass),
564 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
565 "Automatically adjust font size to screen-size.",
566 DEFAULT_PROP_AUTO_ADJUST_SIZE,
567 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
569 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
570 g_param_spec_boolean ("vertical-render", "vertical render",
571 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
572 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
576 gst_base_text_overlay_finalize (GObject * object)
578 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
580 g_free (overlay->default_text);
582 if (overlay->text_image) {
583 g_free (overlay->text_image);
584 overlay->text_image = NULL;
587 if (overlay->layout) {
588 g_object_unref (overlay->layout);
589 overlay->layout = NULL;
592 if (overlay->text_buffer) {
593 gst_buffer_unref (overlay->text_buffer);
594 overlay->text_buffer = NULL;
598 g_cond_free (overlay->cond);
599 overlay->cond = NULL;
602 G_OBJECT_CLASS (parent_class)->finalize (object);
606 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
607 GstBaseTextOverlayClass * klass)
609 GstPadTemplate *template;
610 PangoFontDescription *desc;
613 template = gst_static_pad_template_get (&video_sink_template_factory);
614 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
615 gst_object_unref (template);
616 gst_pad_set_getcaps_function (overlay->video_sinkpad,
617 GST_DEBUG_FUNCPTR (gst_base_text_overlay_getcaps));
618 gst_pad_set_event_function (overlay->video_sinkpad,
619 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_event));
620 gst_pad_set_chain_function (overlay->video_sinkpad,
621 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
622 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
625 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
629 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
630 gst_object_unref (template);
632 gst_pad_set_event_function (overlay->text_sinkpad,
633 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
634 gst_pad_set_chain_function (overlay->text_sinkpad,
635 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
636 gst_pad_set_link_function (overlay->text_sinkpad,
637 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
638 gst_pad_set_unlink_function (overlay->text_sinkpad,
639 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
640 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
644 template = gst_static_pad_template_get (&src_template_factory);
645 overlay->srcpad = gst_pad_new_from_template (template, "src");
646 gst_object_unref (template);
647 gst_pad_set_getcaps_function (overlay->srcpad,
648 GST_DEBUG_FUNCPTR (gst_base_text_overlay_getcaps));
649 gst_pad_set_event_function (overlay->srcpad,
650 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_event));
651 gst_pad_set_query_function (overlay->srcpad,
652 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_query));
653 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
655 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
656 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
658 pango_layout_new (GST_BASE_TEXT_OVERLAY_GET_CLASS
659 (overlay)->pango_context);
661 pango_context_get_font_description (GST_BASE_TEXT_OVERLAY_GET_CLASS
662 (overlay)->pango_context);
663 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
665 overlay->color = DEFAULT_PROP_COLOR;
666 overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
667 overlay->halign = DEFAULT_PROP_HALIGNMENT;
668 overlay->valign = DEFAULT_PROP_VALIGNMENT;
669 overlay->xpad = DEFAULT_PROP_XPAD;
670 overlay->ypad = DEFAULT_PROP_YPAD;
671 overlay->deltax = DEFAULT_PROP_DELTAX;
672 overlay->deltay = DEFAULT_PROP_DELTAY;
673 overlay->xpos = DEFAULT_PROP_XPOS;
674 overlay->ypos = DEFAULT_PROP_YPOS;
676 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
678 overlay->want_shading = DEFAULT_PROP_SHADING;
679 overlay->shading_value = DEFAULT_SHADING_VALUE;
680 overlay->silent = DEFAULT_PROP_SILENT;
681 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
682 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
684 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
685 overlay->need_render = TRUE;
686 overlay->text_image = NULL;
687 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
688 gst_base_text_overlay_update_render_mode (overlay);
693 overlay->text_buffer = NULL;
694 overlay->text_linked = FALSE;
695 overlay->cond = g_cond_new ();
696 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
697 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
701 gst_base_text_overlay_update_wrap_mode (GstBaseTextOverlay * overlay)
703 if (overlay->wrap_mode == GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE) {
704 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
705 pango_layout_set_width (overlay->layout, -1);
709 if (overlay->auto_adjust_size) {
710 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
711 if (overlay->use_vertical_render) {
712 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
716 (overlay->use_vertical_render ? overlay->height : overlay->width) *
720 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
721 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
722 pango_layout_set_width (overlay->layout, width);
723 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
728 gst_base_text_overlay_update_render_mode (GstBaseTextOverlay * overlay)
730 PangoMatrix matrix = PANGO_MATRIX_INIT;
731 PangoContext *context = pango_layout_get_context (overlay->layout);
733 if (overlay->use_vertical_render) {
734 pango_matrix_rotate (&matrix, -90);
735 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
736 pango_context_set_matrix (context, &matrix);
737 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
739 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
740 pango_context_set_matrix (context, &matrix);
741 pango_layout_set_alignment (overlay->layout, overlay->line_align);
746 gst_base_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps)
748 GstBaseTextOverlay *overlay;
749 GstStructure *structure;
751 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
753 structure = gst_caps_get_structure (caps, 0);
754 overlay->have_pango_markup =
755 gst_structure_has_name (structure, "text/x-pango-markup");
757 gst_object_unref (overlay);
762 /* FIXME: upstream nego (e.g. when the video window is resized) */
765 gst_base_text_overlay_setcaps (GstPad * pad, GstCaps * caps)
767 GstBaseTextOverlay *overlay;
768 GstStructure *structure;
769 gboolean ret = FALSE;
772 if (!GST_PAD_IS_SINK (pad))
775 g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
777 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
781 structure = gst_caps_get_structure (caps, 0);
782 fps = gst_structure_get_value (structure, "framerate");
785 && gst_video_format_parse_caps (caps, &overlay->format, &overlay->width,
787 ret = gst_pad_push_event (overlay->srcpad, gst_event_new_caps (caps));
790 overlay->fps_n = gst_value_get_fraction_numerator (fps);
791 overlay->fps_d = gst_value_get_fraction_denominator (fps);
794 GST_OBJECT_LOCK (overlay);
795 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
796 gst_base_text_overlay_update_wrap_mode (overlay);
797 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
798 GST_OBJECT_UNLOCK (overlay);
801 gst_object_unref (overlay);
807 gst_base_text_overlay_set_property (GObject * object, guint prop_id,
808 const GValue * value, GParamSpec * pspec)
810 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
812 GST_OBJECT_LOCK (overlay);
815 g_free (overlay->default_text);
816 overlay->default_text = g_value_dup_string (value);
817 overlay->need_render = TRUE;
820 overlay->want_shading = g_value_get_boolean (value);
823 overlay->xpad = g_value_get_int (value);
826 overlay->ypad = g_value_get_int (value);
829 overlay->deltax = g_value_get_int (value);
832 overlay->deltay = g_value_get_int (value);
835 overlay->xpos = g_value_get_double (value);
838 overlay->ypos = g_value_get_double (value);
841 const gchar *s = g_value_get_string (value);
843 if (s && g_ascii_strcasecmp (s, "left") == 0)
844 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_LEFT;
845 else if (s && g_ascii_strcasecmp (s, "center") == 0)
846 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_CENTER;
847 else if (s && g_ascii_strcasecmp (s, "right") == 0)
848 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
850 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
855 const gchar *s = g_value_get_string (value);
857 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
858 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE;
859 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
860 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM;
861 else if (s && g_ascii_strcasecmp (s, "top") == 0)
862 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
864 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
868 case PROP_VALIGNMENT:
869 overlay->valign = g_value_get_enum (value);
871 case PROP_HALIGNMENT:
872 overlay->halign = g_value_get_enum (value);
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);
882 PangoFontDescription *desc;
883 const gchar *fontdesc_str;
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);
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);
894 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
897 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
901 overlay->color = g_value_get_uint (value);
903 case PROP_OUTLINE_COLOR:
904 overlay->outline_color = g_value_get_uint (value);
907 overlay->silent = g_value_get_boolean (value);
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);
917 overlay->wait_text = g_value_get_boolean (value);
919 case PROP_AUTO_ADJUST_SIZE:
920 overlay->auto_adjust_size = g_value_get_boolean (value);
921 overlay->need_render = TRUE;
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;
931 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
935 overlay->need_render = TRUE;
936 GST_OBJECT_UNLOCK (overlay);
940 gst_base_text_overlay_get_property (GObject * object, guint prop_id,
941 GValue * value, GParamSpec * pspec)
943 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
945 GST_OBJECT_LOCK (overlay);
948 g_value_set_string (value, overlay->default_text);
951 g_value_set_boolean (value, overlay->want_shading);
954 g_value_set_int (value, overlay->xpad);
957 g_value_set_int (value, overlay->ypad);
960 g_value_set_int (value, overlay->deltax);
963 g_value_set_int (value, overlay->deltay);
966 g_value_set_double (value, overlay->xpos);
969 g_value_set_double (value, overlay->ypos);
971 case PROP_VALIGNMENT:
972 g_value_set_enum (value, overlay->valign);
974 case PROP_HALIGNMENT:
975 g_value_set_enum (value, overlay->halign);
978 g_value_set_enum (value, overlay->wrap_mode);
981 g_value_set_boolean (value, overlay->silent);
983 case PROP_LINE_ALIGNMENT:
984 g_value_set_enum (value, overlay->line_align);
987 g_value_set_boolean (value, overlay->wait_text);
989 case PROP_AUTO_ADJUST_SIZE:
990 g_value_set_boolean (value, overlay->auto_adjust_size);
992 case PROP_VERTICAL_RENDER:
993 g_value_set_boolean (value, overlay->use_vertical_render);
996 g_value_set_uint (value, overlay->color);
998 case PROP_OUTLINE_COLOR:
999 g_value_set_uint (value, overlay->outline_color);
1002 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1006 overlay->need_render = TRUE;
1007 GST_OBJECT_UNLOCK (overlay);
1011 gst_base_text_overlay_src_query (GstPad * pad, GstQuery * query)
1013 gboolean ret = FALSE;
1014 GstBaseTextOverlay *overlay = NULL;
1016 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1018 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1020 gst_object_unref (overlay);
1026 gst_base_text_overlay_src_event (GstPad * pad, GstEvent * event)
1028 gboolean ret = FALSE;
1029 GstBaseTextOverlay *overlay = NULL;
1031 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1033 switch (GST_EVENT_TYPE (event)) {
1034 case GST_EVENT_SEEK:{
1037 /* We don't handle seek if we have not text pad */
1038 if (!overlay->text_linked) {
1039 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1040 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1044 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1046 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1048 /* Flush downstream, only for flushing seek */
1049 if (flags & GST_SEEK_FLAG_FLUSH)
1050 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1052 /* Mark ourself as flushing, unblock chains */
1053 GST_OBJECT_LOCK (overlay);
1054 overlay->video_flushing = TRUE;
1055 overlay->text_flushing = TRUE;
1056 gst_base_text_overlay_pop_text (overlay);
1057 GST_OBJECT_UNLOCK (overlay);
1059 /* Seek on each sink pad */
1060 gst_event_ref (event);
1061 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1063 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1065 gst_event_unref (event);
1070 if (overlay->text_linked) {
1071 gst_event_ref (event);
1072 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1073 gst_pad_push_event (overlay->text_sinkpad, event);
1075 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1081 gst_object_unref (overlay);
1087 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1089 GstBaseTextOverlay *overlay;
1093 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1095 if (pad == overlay->srcpad)
1096 otherpad = overlay->video_sinkpad;
1098 otherpad = overlay->srcpad;
1100 /* we can do what the peer can */
1101 caps = gst_pad_peer_get_caps (otherpad, filter);
1103 GstCaps *temp, *templ;
1105 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1107 /* filtered against our padtemplate */
1108 templ = gst_pad_get_pad_template_caps (otherpad);
1109 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1110 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1111 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1112 gst_caps_unref (caps);
1113 gst_caps_unref (templ);
1114 /* this is what we can do */
1117 /* no peer, our padtemplate is enough then */
1118 caps = gst_pad_get_pad_template_caps (pad);
1120 GstCaps *intersection;
1123 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1124 gst_caps_unref (caps);
1125 caps = intersection;
1129 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1131 gst_object_unref (overlay);
1137 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1138 PangoFontDescription * desc)
1140 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1141 overlay->shadow_offset = (double) (font_size) / 13.0;
1142 overlay->outline_offset = (double) (font_size) / 15.0;
1143 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1144 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1147 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1148 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1149 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1150 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1154 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1155 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1162 gint width = overlay->image_width;
1163 gint height = overlay->image_height;
1169 if (xpos + width > overlay->width) {
1170 width = overlay->width - xpos;
1173 if (ypos + height > overlay->height) {
1174 height = overlay->height - ypos;
1177 dest += (ypos / 1) * dest_stride;
1179 for (i = 0; i < height; i++) {
1180 pimage = text_image + 4 * (i * overlay->image_width);
1181 py = dest + i * dest_stride + xpos;
1182 for (j = 0; j < width; j++) {
1183 b = pimage[CAIRO_ARGB_B];
1184 g = pimage[CAIRO_ARGB_G];
1185 r = pimage[CAIRO_ARGB_R];
1186 a = pimage[CAIRO_ARGB_A];
1187 CAIRO_UNPREMULTIPLY (a, r, g, b);
1194 COMP_Y (y, r, g, b);
1196 BLEND (*py++, a, y, x);
1202 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1203 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1204 guint destcb_stride, guint destcr_stride, guint pix_stride)
1209 gushort r1, g1, b1, a1;
1210 guchar *pimage1, *pimage2;
1212 gint width = overlay->image_width - 2;
1213 gint height = overlay->image_height - 2;
1221 if (xpos + width > overlay->width) {
1222 width = overlay->width - xpos;
1225 if (ypos + height > overlay->height) {
1226 height = overlay->height - ypos;
1229 destcb += (ypos / 2) * destcb_stride;
1230 destcr += (ypos / 2) * destcr_stride;
1232 for (i = 0; i < height; i += 2) {
1233 pimage1 = text_image + 4 * (i * overlay->image_width);
1234 pimage2 = pimage1 + 4 * overlay->image_width;
1235 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1236 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1237 for (j = 0; j < width; j += 2) {
1238 b = pimage1[CAIRO_ARGB_B];
1239 g = pimage1[CAIRO_ARGB_G];
1240 r = pimage1[CAIRO_ARGB_R];
1241 a = pimage1[CAIRO_ARGB_A];
1242 CAIRO_UNPREMULTIPLY (a, r, g, b);
1245 b1 = pimage1[CAIRO_ARGB_B];
1246 g1 = pimage1[CAIRO_ARGB_G];
1247 r1 = pimage1[CAIRO_ARGB_R];
1248 a1 = pimage1[CAIRO_ARGB_A];
1249 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1256 b1 = pimage2[CAIRO_ARGB_B];
1257 g1 = pimage2[CAIRO_ARGB_G];
1258 r1 = pimage2[CAIRO_ARGB_R];
1259 a1 = pimage2[CAIRO_ARGB_A];
1260 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1267 /* + 2 for rounding */
1268 b1 = pimage2[CAIRO_ARGB_B];
1269 g1 = pimage2[CAIRO_ARGB_G];
1270 r1 = pimage2[CAIRO_ARGB_R];
1271 a1 = pimage2[CAIRO_ARGB_A];
1272 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1289 COMP_U (cb, r, g, b);
1290 COMP_V (cr, r, g, b);
1293 BLEND (*pcb, a, cb, x);
1295 BLEND (*pcr, a, cr, x);
1304 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1305 const gchar * string, gint textlen)
1308 cairo_surface_t *surface;
1309 PangoRectangle ink_rect, logical_rect;
1310 cairo_matrix_t cairo_matrix;
1312 double scalef = 1.0;
1315 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1317 if (overlay->auto_adjust_size) {
1318 /* 640 pixel is default */
1319 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1321 pango_layout_set_width (overlay->layout, -1);
1322 /* set text on pango layout */
1323 pango_layout_set_markup (overlay->layout, string, textlen);
1325 /* get subtitle image size */
1326 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1328 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1330 if (width + overlay->deltax >
1331 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1333 * subtitle image width is larger then overlay width
1334 * so rearrange overlay wrap mode.
1336 gst_base_text_overlay_update_wrap_mode (overlay);
1337 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1338 width = overlay->width;
1342 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1343 if (height > overlay->height) {
1344 height = overlay->height;
1346 if (overlay->use_vertical_render) {
1347 PangoRectangle rect;
1348 PangoContext *context;
1349 PangoMatrix matrix = PANGO_MATRIX_INIT;
1352 context = pango_layout_get_context (overlay->layout);
1354 pango_matrix_rotate (&matrix, -90);
1356 rect.x = rect.y = 0;
1358 rect.height = height;
1359 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1360 matrix.x0 = -rect.x;
1361 matrix.y0 = -rect.y;
1363 pango_context_set_matrix (context, &matrix);
1365 cairo_matrix.xx = matrix.xx;
1366 cairo_matrix.yx = matrix.yx;
1367 cairo_matrix.xy = matrix.xy;
1368 cairo_matrix.yy = matrix.yy;
1369 cairo_matrix.x0 = matrix.x0;
1370 cairo_matrix.y0 = matrix.y0;
1371 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1377 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1380 /* reallocate surface */
1381 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1383 surface = cairo_image_surface_create_for_data (overlay->text_image,
1384 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1385 cr = cairo_create (surface);
1388 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1391 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1393 if (overlay->want_shading)
1394 cairo_paint_with_alpha (cr, overlay->shading_value);
1396 /* apply transformations */
1397 cairo_set_matrix (cr, &cairo_matrix);
1399 /* FIXME: We use show_layout everywhere except for the surface
1400 * because it's really faster and internally does all kinds of
1401 * caching. Unfortunately we have to paint to a cairo path for
1402 * the outline and this is slow. Once Pango supports user fonts
1403 * we should use them, see
1404 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1406 * Idea would the be, to create a cairo user font that
1407 * does shadow, outline, text painting in the
1408 * render_glyph function.
1411 /* draw shadow text */
1413 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1414 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1415 pango_cairo_show_layout (cr, overlay->layout);
1418 a = (overlay->outline_color >> 24) & 0xff;
1419 r = (overlay->outline_color >> 16) & 0xff;
1420 g = (overlay->outline_color >> 8) & 0xff;
1421 b = (overlay->outline_color >> 0) & 0xff;
1423 /* draw outline text */
1425 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1426 cairo_set_line_width (cr, overlay->outline_offset);
1427 pango_cairo_layout_path (cr, overlay->layout);
1431 a = (overlay->color >> 24) & 0xff;
1432 r = (overlay->color >> 16) & 0xff;
1433 g = (overlay->color >> 8) & 0xff;
1434 b = (overlay->color >> 0) & 0xff;
1438 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1439 pango_cairo_show_layout (cr, overlay->layout);
1443 cairo_surface_destroy (surface);
1444 overlay->image_width = width;
1445 overlay->image_height = height;
1446 overlay->baseline_y = ink_rect.y;
1447 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1454 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1455 guchar * dest, gint x0, gint x1, gint y0, gint y1)
1457 gint i, j, dest_stride;
1459 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1462 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1463 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1465 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1466 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1468 for (i = y0; i < y1; ++i) {
1469 for (j = x0; j < x1; ++j) {
1470 gint y = dest[(i * dest_stride) + j] + overlay->shading_value;
1472 dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1478 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1479 guchar * dest, gint x0, gint x1, gint y0, gint y1)
1482 guint dest_stride, pixel_stride, component_offset;
1484 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1486 pixel_stride = gst_video_format_get_pixel_stride (overlay->format, 0);
1488 gst_video_format_get_component_offset (overlay->format, 0, overlay->width,
1491 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1492 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1494 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1495 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1498 x0 = gst_video_format_get_component_width (overlay->format, 0, x0);
1500 x1 = gst_video_format_get_component_width (overlay->format, 0, x1);
1503 y0 = gst_video_format_get_component_height (overlay->format, 0, y0);
1505 y1 = gst_video_format_get_component_height (overlay->format, 0, y1);
1507 for (i = y0; i < y1; i++) {
1508 for (j = x0; j < x1; j++) {
1512 y_pos = (i * dest_stride) + j * pixel_stride + component_offset;
1513 y = dest[y_pos] + overlay->shading_value;
1515 dest[y_pos] = CLAMP (y, 0, 255);
1520 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1521 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1522 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1524 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay, guchar * dest,
1525 gint x0, gint x1, gint y0, gint y1)
1529 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1530 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1532 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1533 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1535 for (i = y0; i < y1; i++) {
1536 for (j = x0; j < x1; j++) {
1539 y_pos = (i * 4 * overlay->width) + j * 4;
1540 for (k = 0; k < 4; k++) {
1541 y = dest[y_pos + k] + overlay->shading_value;
1542 dest[y_pos + k] = CLAMP (y, 0, 255);
1548 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1549 static inline void \
1550 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, guchar * dest, \
1551 gint x0, gint x1, gint y0, gint y1) \
1555 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1556 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1558 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1559 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1561 for (i = y0; i < y1; i++) {\
1562 for (j = x0; j < x1; j++) {\
1564 y_pos = (i * 4 * overlay->width) + j * 4;\
1565 for (k = OFFSET; k < 3+OFFSET; k++) {\
1566 y = dest[y_pos + k] + overlay->shading_value;\
1567 dest[y_pos + k] = CLAMP (y, 0, 255);\
1572 ARGB_SHADE_FUNCTION (ARGB, 1);
1573 ARGB_SHADE_FUNCTION (ABGR, 1);
1574 ARGB_SHADE_FUNCTION (RGBA, 0);
1575 ARGB_SHADE_FUNCTION (BGRA, 0);
1579 * - use proper strides and offset for I420
1580 * - don't draw over the edge of the picture (try a longer
1581 * text with a huge font size)
1585 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1586 guint8 * yuv_pixels, gint xpos, gint ypos)
1588 int y_stride, uv_stride;
1589 int u_offset, v_offset;
1592 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1593 * to a boundary of integer number of U/V pixels:
1595 xpos = GST_ROUND_UP_2 (xpos);
1596 ypos = GST_ROUND_UP_2 (ypos);
1599 h = overlay->height;
1601 y_stride = gst_video_format_get_row_stride (overlay->format, 0, w);
1602 uv_stride = gst_video_format_get_row_stride (overlay->format, 1, w);
1603 u_offset = gst_video_format_get_component_offset (overlay->format, 1, w, h);
1604 v_offset = gst_video_format_get_component_offset (overlay->format, 2, w, h);
1606 gst_base_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos,
1607 overlay->text_image, y_stride);
1608 gst_base_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1609 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, uv_stride,
1614 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1615 guint8 * yuv_pixels, gint xpos, gint ypos)
1617 int y_stride, u_stride, v_stride;
1618 int u_offset, v_offset;
1621 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1622 * to a boundary of integer number of U/V pixels:
1624 xpos = GST_ROUND_UP_2 (xpos);
1625 ypos = GST_ROUND_UP_2 (ypos);
1628 h = overlay->height;
1630 y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, w);
1631 u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, w);
1632 v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, w);
1634 gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1, w, h);
1636 gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h);
1638 gst_base_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos,
1639 overlay->text_image, y_stride);
1640 gst_base_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1641 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride,
1646 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1647 guint8 * yuv_pixels, gint xpos, gint ypos)
1654 guchar *pimage, *dest;
1656 /* because U/V is 2x horizontally subsampled, we need to round to a
1657 * boundary of integer number of U/V pixels in x dimension:
1659 xpos = GST_ROUND_UP_2 (xpos);
1661 w = overlay->image_width - 2;
1662 h = overlay->image_height - 2;
1668 if (xpos + w > overlay->width) {
1669 w = overlay->width - xpos;
1672 if (ypos + h > overlay->height) {
1673 h = overlay->height - ypos;
1676 for (i = 0; i < h; i++) {
1677 pimage = overlay->text_image + i * overlay->image_width * 4;
1678 dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1679 for (j = 0; j < w; j += 2) {
1680 b0 = pimage[CAIRO_ARGB_B];
1681 g0 = pimage[CAIRO_ARGB_G];
1682 r0 = pimage[CAIRO_ARGB_R];
1683 a0 = pimage[CAIRO_ARGB_A];
1684 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1687 b1 = pimage[CAIRO_ARGB_B];
1688 g1 = pimage[CAIRO_ARGB_G];
1689 r1 = pimage[CAIRO_ARGB_R];
1690 a1 = pimage[CAIRO_ARGB_A];
1691 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1701 COMP_Y (y0, r0, g0, b0);
1702 COMP_Y (y1, r1, g1, b1);
1712 COMP_U (u, r0, g0, b0);
1713 COMP_V (v, r0, g0, b0);
1715 BLEND (*dest, a0, u, *dest);
1717 BLEND (*dest, a0, y0, *dest);
1719 BLEND (*dest, a0, v, *dest);
1721 BLEND (*dest, a0, y1, *dest);
1728 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1729 guint8 * rgb_pixels, gint xpos, gint ypos)
1735 guchar *pimage, *dest;
1737 w = overlay->image_width;
1738 h = overlay->image_height;
1744 if (xpos + w > overlay->width) {
1745 w = overlay->width - xpos;
1748 if (ypos + h > overlay->height) {
1749 h = overlay->height - ypos;
1752 for (i = 0; i < h; i++) {
1753 pimage = overlay->text_image + i * overlay->image_width * 4;
1754 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1755 for (j = 0; j < w; j++) {
1756 a = pimage[CAIRO_ARGB_A];
1757 b = pimage[CAIRO_ARGB_B];
1758 g = pimage[CAIRO_ARGB_G];
1759 r = pimage[CAIRO_ARGB_R];
1761 CAIRO_UNPREMULTIPLY (a, r, g, b);
1763 // convert background to yuv
1764 COMP_Y (y, r, g, b);
1765 COMP_U (u, r, g, b);
1766 COMP_V (v, r, g, b);
1768 // preform text "OVER" background alpha compositing
1769 a1 = a + (dest[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1770 OVER (dest[1], a, y, dest[0], dest[1], a1);
1771 OVER (dest[2], a, u, dest[0], dest[2], a1);
1772 OVER (dest[3], a, v, dest[0], dest[3], a1);
1773 dest[0] = a1 - 1; // remove the temporary 1 we added
1781 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1782 static inline void \
1783 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1784 guint8 * rgb_pixels, gint xpos, gint ypos) \
1789 guchar *pimage, *dest; \
1791 w = overlay->image_width; \
1792 h = overlay->image_height; \
1798 if (xpos + w > overlay->width) { \
1799 w = overlay->width - xpos; \
1802 if (ypos + h > overlay->height) { \
1803 h = overlay->height - ypos; \
1806 for (i = 0; i < h; i++) { \
1807 pimage = overlay->text_image + i * overlay->image_width * 4; \
1808 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1809 for (j = 0; j < w; j++) { \
1810 a = pimage[CAIRO_ARGB_A]; \
1811 b = pimage[CAIRO_ARGB_B]; \
1812 g = pimage[CAIRO_ARGB_G]; \
1813 r = pimage[CAIRO_ARGB_R]; \
1814 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1815 b = (b*a + dest[B] * (255-a)) / 255; \
1816 g = (g*a + dest[G] * (255-a)) / 255; \
1817 r = (r*a + dest[R] * (255-a)) / 255; \
1827 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1828 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1829 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1830 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1832 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1833 static inline void \
1834 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1835 guint8 * rgb_pixels, gint xpos, gint ypos) \
1837 int a, r, g, b, a1; \
1840 guchar *pimage, *dest; \
1842 w = overlay->image_width; \
1843 h = overlay->image_height; \
1849 if (xpos + w > overlay->width) { \
1850 w = overlay->width - xpos; \
1853 if (ypos + h > overlay->height) { \
1854 h = overlay->height - ypos; \
1857 for (i = 0; i < h; i++) { \
1858 pimage = overlay->text_image + i * overlay->image_width * 4; \
1859 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1860 for (j = 0; j < w; j++) { \
1861 a = pimage[CAIRO_ARGB_A]; \
1862 b = pimage[CAIRO_ARGB_B]; \
1863 g = pimage[CAIRO_ARGB_G]; \
1864 r = pimage[CAIRO_ARGB_R]; \
1865 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1866 a1 = a + (dest[A] * (255 - a)) / 255 + 1; \
1867 OVER (dest[R], a, r, dest[0], dest[R], a1); \
1868 OVER (dest[G], a, g, dest[0], dest[G], a1); \
1869 OVER (dest[B], a, b, dest[0], dest[B], a1); \
1876 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1877 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1878 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1879 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1882 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1883 const gchar * text, gint textlen)
1887 if (!overlay->need_render) {
1888 GST_DEBUG ("Using previously rendered text.");
1892 /* -1 is the whole string */
1893 if (text != NULL && textlen < 0) {
1894 textlen = strlen (text);
1898 string = g_strndup (text, textlen);
1899 } else { /* empty string */
1900 string = g_strdup (" ");
1902 g_strdelimit (string, "\r\t", ' ');
1903 textlen = strlen (string);
1905 /* FIXME: should we check for UTF-8 here? */
1907 GST_DEBUG ("Rendering '%s'", string);
1908 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1912 overlay->need_render = FALSE;
1915 static GstFlowReturn
1916 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1917 GstBuffer * video_frame)
1921 GstBaseTextOverlayVAlign valign;
1922 GstBaseTextOverlayHAlign halign;
1926 width = overlay->image_width;
1927 height = overlay->image_height;
1929 video_frame = gst_buffer_make_writable (video_frame);
1931 data = gst_buffer_map (video_frame, &size, NULL, GST_MAP_WRITE);
1933 if (overlay->use_vertical_render)
1934 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1936 halign = overlay->halign;
1939 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1940 xpos = overlay->xpad;
1942 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1943 xpos = (overlay->width - width) / 2;
1945 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1946 xpos = overlay->width - width - overlay->xpad;
1948 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1949 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1950 xpos = CLAMP (xpos, 0, overlay->width - width);
1957 xpos += overlay->deltax;
1959 if (overlay->use_vertical_render)
1960 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1962 valign = overlay->valign;
1965 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1966 ypos = overlay->height - height - overlay->ypad;
1968 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1969 ypos = overlay->height - (height + overlay->ypad);
1971 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1972 ypos = overlay->ypad;
1974 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1975 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1976 ypos = CLAMP (ypos, 0, overlay->height - height);
1978 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1979 ypos = (overlay->height - height) / 2;
1982 ypos = overlay->ypad;
1985 ypos += overlay->deltay;
1987 /* shaded background box */
1988 if (overlay->want_shading) {
1989 switch (overlay->format) {
1990 case GST_VIDEO_FORMAT_I420:
1991 case GST_VIDEO_FORMAT_NV12:
1992 case GST_VIDEO_FORMAT_NV21:
1993 gst_base_text_overlay_shade_planar_Y (overlay, data,
1994 xpos, xpos + overlay->image_width,
1995 ypos, ypos + overlay->image_height);
1997 case GST_VIDEO_FORMAT_AYUV:
1998 case GST_VIDEO_FORMAT_UYVY:
1999 gst_base_text_overlay_shade_packed_Y (overlay, data,
2000 xpos, xpos + overlay->image_width,
2001 ypos, ypos + overlay->image_height);
2003 case GST_VIDEO_FORMAT_xRGB:
2004 gst_base_text_overlay_shade_xRGB (overlay, data,
2005 xpos, xpos + overlay->image_width,
2006 ypos, ypos + overlay->image_height);
2008 case GST_VIDEO_FORMAT_xBGR:
2009 gst_base_text_overlay_shade_xBGR (overlay, data,
2010 xpos, xpos + overlay->image_width,
2011 ypos, ypos + overlay->image_height);
2013 case GST_VIDEO_FORMAT_BGRx:
2014 gst_base_text_overlay_shade_BGRx (overlay, data,
2015 xpos, xpos + overlay->image_width,
2016 ypos, ypos + overlay->image_height);
2018 case GST_VIDEO_FORMAT_RGBx:
2019 gst_base_text_overlay_shade_RGBx (overlay, data,
2020 xpos, xpos + overlay->image_width,
2021 ypos, ypos + overlay->image_height);
2023 case GST_VIDEO_FORMAT_ARGB:
2024 gst_base_text_overlay_shade_ARGB (overlay, data,
2025 xpos, xpos + overlay->image_width,
2026 ypos, ypos + overlay->image_height);
2028 case GST_VIDEO_FORMAT_ABGR:
2029 gst_base_text_overlay_shade_ABGR (overlay, data,
2030 xpos, xpos + overlay->image_width,
2031 ypos, ypos + overlay->image_height);
2033 case GST_VIDEO_FORMAT_RGBA:
2034 gst_base_text_overlay_shade_RGBA (overlay, data,
2035 xpos, xpos + overlay->image_width,
2036 ypos, ypos + overlay->image_height);
2038 case GST_VIDEO_FORMAT_BGRA:
2039 gst_base_text_overlay_shade_BGRA (overlay, data,
2040 xpos, xpos + overlay->image_width,
2041 ypos, ypos + overlay->image_height);
2044 g_assert_not_reached ();
2051 if (overlay->text_image) {
2052 switch (overlay->format) {
2053 case GST_VIDEO_FORMAT_I420:
2054 gst_base_text_overlay_blit_I420 (overlay, data, xpos, ypos);
2056 case GST_VIDEO_FORMAT_NV12:
2057 case GST_VIDEO_FORMAT_NV21:
2058 gst_base_text_overlay_blit_NV12_NV21 (overlay, data, xpos, ypos);
2060 case GST_VIDEO_FORMAT_UYVY:
2061 gst_base_text_overlay_blit_UYVY (overlay, data, xpos, ypos);
2063 case GST_VIDEO_FORMAT_AYUV:
2064 gst_base_text_overlay_blit_AYUV (overlay, data, xpos, ypos);
2066 case GST_VIDEO_FORMAT_BGRx:
2067 gst_base_text_overlay_blit_BGRx (overlay, data, xpos, ypos);
2069 case GST_VIDEO_FORMAT_xRGB:
2070 gst_base_text_overlay_blit_xRGB (overlay, data, xpos, ypos);
2072 case GST_VIDEO_FORMAT_RGBx:
2073 gst_base_text_overlay_blit_RGBx (overlay, data, xpos, ypos);
2075 case GST_VIDEO_FORMAT_xBGR:
2076 gst_base_text_overlay_blit_xBGR (overlay, data, xpos, ypos);
2078 case GST_VIDEO_FORMAT_ARGB:
2079 gst_base_text_overlay_blit_ARGB (overlay, data, xpos, ypos);
2081 case GST_VIDEO_FORMAT_ABGR:
2082 gst_base_text_overlay_blit_ABGR (overlay, data, xpos, ypos);
2084 case GST_VIDEO_FORMAT_RGBA:
2085 gst_base_text_overlay_blit_RGBA (overlay, data, xpos, ypos);
2087 case GST_VIDEO_FORMAT_BGRA:
2088 gst_base_text_overlay_blit_BGRA (overlay, data, xpos, ypos);
2091 g_assert_not_reached ();
2094 gst_buffer_unmap (video_frame, data, size);
2096 return gst_pad_push (overlay->srcpad, video_frame);
2099 static GstPadLinkReturn
2100 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2102 GstBaseTextOverlay *overlay;
2104 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2106 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2108 overlay->text_linked = TRUE;
2110 gst_object_unref (overlay);
2112 return GST_PAD_LINK_OK;
2116 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2118 GstBaseTextOverlay *overlay;
2120 /* don't use gst_pad_get_parent() here, will deadlock */
2121 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2123 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2125 overlay->text_linked = FALSE;
2127 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2131 gst_base_text_overlay_text_event (GstPad * pad, GstEvent * event)
2133 gboolean ret = FALSE;
2134 GstBaseTextOverlay *overlay = NULL;
2136 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2138 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2140 switch (GST_EVENT_TYPE (event)) {
2141 case GST_EVENT_CAPS:
2145 gst_event_parse_caps (event, &caps);
2146 ret = gst_base_text_overlay_setcaps_txt (pad, caps);
2147 gst_event_unref (event);
2150 case GST_EVENT_SEGMENT:
2152 const GstSegment *segment;
2154 overlay->text_eos = FALSE;
2156 gst_event_parse_segment (event, &segment);
2158 if (segment->format == GST_FORMAT_TIME) {
2159 GST_OBJECT_LOCK (overlay);
2160 gst_segment_copy_into (segment, &overlay->text_segment);
2161 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2162 &overlay->text_segment);
2163 GST_OBJECT_UNLOCK (overlay);
2165 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2166 ("received non-TIME newsegment event on text input"));
2169 gst_event_unref (event);
2172 /* wake up the video chain, it might be waiting for a text buffer or
2173 * a text segment update */
2174 GST_OBJECT_LOCK (overlay);
2175 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2176 GST_OBJECT_UNLOCK (overlay);
2179 case GST_EVENT_FLUSH_STOP:
2180 GST_OBJECT_LOCK (overlay);
2181 GST_INFO_OBJECT (overlay, "text flush stop");
2182 overlay->text_flushing = FALSE;
2183 overlay->text_eos = FALSE;
2184 gst_base_text_overlay_pop_text (overlay);
2185 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2186 GST_OBJECT_UNLOCK (overlay);
2187 gst_event_unref (event);
2190 case GST_EVENT_FLUSH_START:
2191 GST_OBJECT_LOCK (overlay);
2192 GST_INFO_OBJECT (overlay, "text flush start");
2193 overlay->text_flushing = TRUE;
2194 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2195 GST_OBJECT_UNLOCK (overlay);
2196 gst_event_unref (event);
2200 GST_OBJECT_LOCK (overlay);
2201 overlay->text_eos = TRUE;
2202 GST_INFO_OBJECT (overlay, "text EOS");
2203 /* wake up the video chain, it might be waiting for a text buffer or
2204 * a text segment update */
2205 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2206 GST_OBJECT_UNLOCK (overlay);
2207 gst_event_unref (event);
2211 ret = gst_pad_event_default (pad, event);
2215 gst_object_unref (overlay);
2221 gst_base_text_overlay_video_event (GstPad * pad, GstEvent * event)
2223 gboolean ret = FALSE;
2224 GstBaseTextOverlay *overlay = NULL;
2226 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2228 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2230 switch (GST_EVENT_TYPE (event)) {
2231 case GST_EVENT_CAPS:
2235 gst_event_parse_caps (event, &caps);
2236 ret = gst_base_text_overlay_setcaps (pad, caps);
2237 gst_event_unref (event);
2240 case GST_EVENT_SEGMENT:
2242 const GstSegment *segment;
2244 GST_DEBUG_OBJECT (overlay, "received new segment");
2246 gst_event_parse_segment (event, &segment);
2248 if (segment->format == GST_FORMAT_TIME) {
2249 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2252 gst_segment_copy_into (segment, &overlay->segment);
2254 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2255 ("received non-TIME newsegment event on video input"));
2258 ret = gst_pad_event_default (pad, event);
2262 GST_OBJECT_LOCK (overlay);
2263 GST_INFO_OBJECT (overlay, "video EOS");
2264 overlay->video_eos = TRUE;
2265 GST_OBJECT_UNLOCK (overlay);
2266 ret = gst_pad_event_default (pad, event);
2268 case GST_EVENT_FLUSH_START:
2269 GST_OBJECT_LOCK (overlay);
2270 GST_INFO_OBJECT (overlay, "video flush start");
2271 overlay->video_flushing = TRUE;
2272 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2273 GST_OBJECT_UNLOCK (overlay);
2274 ret = gst_pad_event_default (pad, event);
2276 case GST_EVENT_FLUSH_STOP:
2277 GST_OBJECT_LOCK (overlay);
2278 GST_INFO_OBJECT (overlay, "video flush stop");
2279 overlay->video_flushing = FALSE;
2280 overlay->video_eos = FALSE;
2281 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2282 GST_OBJECT_UNLOCK (overlay);
2283 ret = gst_pad_event_default (pad, event);
2286 ret = gst_pad_event_default (pad, event);
2290 gst_object_unref (overlay);
2295 /* Called with lock held */
2297 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2299 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2301 if (overlay->text_buffer) {
2302 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2303 overlay->text_buffer);
2304 gst_buffer_unref (overlay->text_buffer);
2305 overlay->text_buffer = NULL;
2308 /* Let the text task know we used that buffer */
2309 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2312 /* We receive text buffers here. If they are out of segment we just ignore them.
2313 If the buffer is in our segment we keep it internally except if another one
2314 is already waiting here, in that case we wait that it gets kicked out */
2315 static GstFlowReturn
2316 gst_base_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
2318 GstFlowReturn ret = GST_FLOW_OK;
2319 GstBaseTextOverlay *overlay = NULL;
2320 gboolean in_seg = FALSE;
2321 guint64 clip_start = 0, clip_stop = 0;
2323 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2325 GST_OBJECT_LOCK (overlay);
2327 if (overlay->text_flushing) {
2328 GST_OBJECT_UNLOCK (overlay);
2329 ret = GST_FLOW_WRONG_STATE;
2330 GST_LOG_OBJECT (overlay, "text flushing");
2334 if (overlay->text_eos) {
2335 GST_OBJECT_UNLOCK (overlay);
2336 ret = GST_FLOW_UNEXPECTED;
2337 GST_LOG_OBJECT (overlay, "text EOS");
2341 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2342 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2343 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2344 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2345 GST_BUFFER_DURATION (buffer)));
2347 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2350 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2351 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2353 stop = GST_CLOCK_TIME_NONE;
2355 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2356 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2362 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2363 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2364 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2365 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2367 /* Wait for the previous buffer to go away */
2368 while (overlay->text_buffer != NULL) {
2369 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2370 GST_DEBUG_PAD_NAME (pad));
2371 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2372 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2373 if (overlay->text_flushing) {
2374 GST_OBJECT_UNLOCK (overlay);
2375 ret = GST_FLOW_WRONG_STATE;
2380 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2381 overlay->text_segment.position = clip_start;
2383 overlay->text_buffer = buffer;
2384 /* That's a new text buffer we need to render */
2385 overlay->need_render = TRUE;
2387 /* in case the video chain is waiting for a text buffer, wake it up */
2388 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2391 GST_OBJECT_UNLOCK (overlay);
2398 static GstFlowReturn
2399 gst_base_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
2401 GstBaseTextOverlayClass *klass;
2402 GstBaseTextOverlay *overlay;
2403 GstFlowReturn ret = GST_FLOW_OK;
2404 gboolean in_seg = FALSE;
2405 guint64 start, stop, clip_start = 0, clip_stop = 0;
2408 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2409 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2411 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2412 goto missing_timestamp;
2414 /* ignore buffers that are outside of the current segment */
2415 start = GST_BUFFER_TIMESTAMP (buffer);
2417 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2418 stop = GST_CLOCK_TIME_NONE;
2420 stop = start + GST_BUFFER_DURATION (buffer);
2423 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2424 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2425 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2427 /* segment_clip() will adjust start unconditionally to segment_start if
2428 * no stop time is provided, so handle this ourselves */
2429 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2430 goto out_of_segment;
2432 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2433 &clip_start, &clip_stop);
2436 goto out_of_segment;
2438 /* if the buffer is only partially in the segment, fix up stamps */
2439 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2440 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2441 buffer = gst_buffer_make_writable (buffer);
2442 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2444 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2447 /* now, after we've done the clipping, fix up end time if there's no
2448 * duration (we only use those estimated values internally though, we
2449 * don't want to set bogus values on the buffer itself) */
2453 gint fps_num, fps_denom;
2455 /* FIXME, store this in setcaps */
2456 caps = gst_pad_get_current_caps (pad);
2457 s = gst_caps_get_structure (caps, 0);
2458 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2459 fps_num && fps_denom) {
2460 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2461 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2463 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2464 stop = start + 1; /* we need to assume some interval */
2466 gst_caps_unref (caps);
2469 gst_object_sync_values (G_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2473 GST_OBJECT_LOCK (overlay);
2475 if (overlay->video_flushing)
2478 if (overlay->video_eos)
2481 if (overlay->silent) {
2482 GST_OBJECT_UNLOCK (overlay);
2483 ret = gst_pad_push (overlay->srcpad, buffer);
2485 /* Update position */
2486 overlay->segment.position = clip_start;
2491 /* Text pad not linked, rendering internal text */
2492 if (!overlay->text_linked) {
2493 if (klass->get_text) {
2494 text = klass->get_text (overlay, buffer);
2496 text = g_strdup (overlay->default_text);
2499 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2500 "text: '%s'", GST_STR_NULL (text));
2502 GST_OBJECT_UNLOCK (overlay);
2504 if (text != NULL && *text != '\0') {
2505 /* Render and push */
2506 gst_base_text_overlay_render_text (overlay, text, -1);
2507 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2509 /* Invalid or empty string */
2510 ret = gst_pad_push (overlay->srcpad, buffer);
2513 /* Text pad linked, check if we have a text buffer queued */
2514 if (overlay->text_buffer) {
2515 gboolean pop_text = FALSE, valid_text_time = TRUE;
2516 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2517 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2518 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2519 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2520 GstClockTime vid_running_time, vid_running_time_end;
2522 /* if the text buffer isn't stamped right, pop it off the
2523 * queue and display it for the current video frame only */
2524 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2525 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2526 GST_WARNING_OBJECT (overlay,
2527 "Got text buffer with invalid timestamp or duration");
2529 valid_text_time = FALSE;
2531 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2532 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2536 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2538 vid_running_time_end =
2539 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2542 /* If timestamp and duration are valid */
2543 if (valid_text_time) {
2545 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2547 text_running_time_end =
2548 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2552 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2553 GST_TIME_ARGS (text_running_time),
2554 GST_TIME_ARGS (text_running_time_end));
2555 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2556 GST_TIME_ARGS (vid_running_time),
2557 GST_TIME_ARGS (vid_running_time_end));
2559 /* Text too old or in the future */
2560 if (valid_text_time && text_running_time_end <= vid_running_time) {
2561 /* text buffer too old, get rid of it and do nothing */
2562 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2564 gst_base_text_overlay_pop_text (overlay);
2565 GST_OBJECT_UNLOCK (overlay);
2566 goto wait_for_text_buf;
2567 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2568 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2569 GST_OBJECT_UNLOCK (overlay);
2570 /* Push the video frame */
2571 ret = gst_pad_push (overlay->srcpad, buffer);
2573 gchar *in_text, *otext;
2574 gsize in_size, osize;
2577 gst_buffer_map (overlay->text_buffer, &osize, NULL, GST_MAP_READ);
2581 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2582 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2583 * here on purpose, this is something that needs fixing upstream */
2584 if (!g_utf8_validate (in_text, in_size, NULL)) {
2585 const gchar *end = NULL;
2587 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2588 in_text = g_strndup (in_text, in_size);
2589 while (!g_utf8_validate (in_text, in_size, &end) && end)
2590 *((gchar *) end) = '*';
2593 /* Get the string */
2594 if (overlay->have_pango_markup) {
2595 text = g_strndup (in_text, in_size);
2597 text = g_markup_escape_text (in_text, in_size);
2600 if (text != NULL && *text != '\0') {
2601 gint text_len = strlen (text);
2603 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2604 text[text_len - 1] == '\r')) {
2607 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2608 gst_base_text_overlay_render_text (overlay, text, text_len);
2610 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2611 gst_base_text_overlay_render_text (overlay, " ", 1);
2613 gst_buffer_unmap (overlay->text_buffer, otext, osize);
2615 if (in_text != otext)
2618 GST_OBJECT_UNLOCK (overlay);
2619 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2621 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2622 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2627 GST_OBJECT_LOCK (overlay);
2628 gst_base_text_overlay_pop_text (overlay);
2629 GST_OBJECT_UNLOCK (overlay);
2632 gboolean wait_for_text_buf = TRUE;
2634 if (overlay->text_eos)
2635 wait_for_text_buf = FALSE;
2637 if (!overlay->wait_text)
2638 wait_for_text_buf = FALSE;
2640 /* Text pad linked, but no text buffer available - what now? */
2641 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2642 GstClockTime text_start_running_time, text_position_running_time;
2643 GstClockTime vid_running_time;
2646 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2647 GST_BUFFER_TIMESTAMP (buffer));
2648 text_start_running_time =
2649 gst_segment_to_running_time (&overlay->text_segment,
2650 GST_FORMAT_TIME, overlay->text_segment.start);
2651 text_position_running_time =
2652 gst_segment_to_running_time (&overlay->text_segment,
2653 GST_FORMAT_TIME, overlay->text_segment.position);
2655 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2656 vid_running_time < text_start_running_time) ||
2657 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2658 vid_running_time < text_position_running_time)) {
2659 wait_for_text_buf = FALSE;
2663 if (wait_for_text_buf) {
2664 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2665 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2666 GST_DEBUG_OBJECT (overlay, "resuming");
2667 GST_OBJECT_UNLOCK (overlay);
2668 goto wait_for_text_buf;
2670 GST_OBJECT_UNLOCK (overlay);
2671 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2672 ret = gst_pad_push (overlay->srcpad, buffer);
2679 /* Update position */
2680 overlay->segment.position = clip_start;
2686 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2687 gst_buffer_unref (buffer);
2693 GST_OBJECT_UNLOCK (overlay);
2694 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2695 gst_buffer_unref (buffer);
2696 return GST_FLOW_WRONG_STATE;
2700 GST_OBJECT_UNLOCK (overlay);
2701 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2702 gst_buffer_unref (buffer);
2703 return GST_FLOW_UNEXPECTED;
2707 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2708 gst_buffer_unref (buffer);
2713 static GstStateChangeReturn
2714 gst_base_text_overlay_change_state (GstElement * element,
2715 GstStateChange transition)
2717 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2718 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2720 switch (transition) {
2721 case GST_STATE_CHANGE_PAUSED_TO_READY:
2722 GST_OBJECT_LOCK (overlay);
2723 overlay->text_flushing = TRUE;
2724 overlay->video_flushing = TRUE;
2725 /* pop_text will broadcast on the GCond and thus also make the video
2726 * chain exit if it's waiting for a text buffer */
2727 gst_base_text_overlay_pop_text (overlay);
2728 GST_OBJECT_UNLOCK (overlay);
2734 ret = parent_class->change_state (element, transition);
2735 if (ret == GST_STATE_CHANGE_FAILURE)
2738 switch (transition) {
2739 case GST_STATE_CHANGE_READY_TO_PAUSED:
2740 GST_OBJECT_LOCK (overlay);
2741 overlay->text_flushing = FALSE;
2742 overlay->video_flushing = FALSE;
2743 overlay->video_eos = FALSE;
2744 overlay->text_eos = FALSE;
2745 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2746 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2747 GST_OBJECT_UNLOCK (overlay);
2757 plugin_init (GstPlugin * plugin)
2759 gst_controller_init (NULL, NULL);
2761 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2762 GST_TYPE_TEXT_OVERLAY) ||
2763 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2764 GST_TYPE_TIME_OVERLAY) ||
2765 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2766 GST_TYPE_CLOCK_OVERLAY) ||
2767 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2768 GST_TYPE_TEXT_RENDER)) {
2772 /*texttestsrc_plugin_init(module, plugin); */
2774 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2779 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2780 "pango", "Pango-based text rendering and overlay", plugin_init,
2781 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)