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 static GstStaticPadTemplate src_template_factory =
196 GST_STATIC_PAD_TEMPLATE ("src",
199 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";"
200 GST_VIDEO_CAPS_RGBx ";"
201 GST_VIDEO_CAPS_xRGB ";"
202 GST_VIDEO_CAPS_xBGR ";"
203 GST_VIDEO_CAPS_RGBA ";"
204 GST_VIDEO_CAPS_BGRA ";"
205 GST_VIDEO_CAPS_ARGB ";"
206 GST_VIDEO_CAPS_ABGR ";"
207 GST_VIDEO_CAPS_YUV ("{AYUV, I420, UYVY, NV12, NV21}"))
210 static GstStaticPadTemplate video_sink_template_factory =
211 GST_STATIC_PAD_TEMPLATE ("video_sink",
214 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";"
215 GST_VIDEO_CAPS_RGBx ";"
216 GST_VIDEO_CAPS_xRGB ";"
217 GST_VIDEO_CAPS_xBGR ";"
218 GST_VIDEO_CAPS_RGBA ";"
219 GST_VIDEO_CAPS_BGRA ";"
220 GST_VIDEO_CAPS_ARGB ";"
221 GST_VIDEO_CAPS_ABGR ";"
222 GST_VIDEO_CAPS_YUV ("{AYUV, I420, UYVY, NV12, NV21}"))
225 #define GST_TYPE_BASE_TEXT_OVERLAY_VALIGN (gst_base_text_overlay_valign_get_type())
227 gst_base_text_overlay_valign_get_type (void)
229 static GType base_text_overlay_valign_type = 0;
230 static const GEnumValue base_text_overlay_valign[] = {
231 {GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"},
232 {GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"},
233 {GST_BASE_TEXT_OVERLAY_VALIGN_TOP, "top", "top"},
234 {GST_BASE_TEXT_OVERLAY_VALIGN_POS, "position", "position"},
235 {GST_BASE_TEXT_OVERLAY_VALIGN_CENTER, "center", "center"},
239 if (!base_text_overlay_valign_type) {
240 base_text_overlay_valign_type =
241 g_enum_register_static ("GstBaseTextOverlayVAlign",
242 base_text_overlay_valign);
244 return base_text_overlay_valign_type;
247 #define GST_TYPE_BASE_TEXT_OVERLAY_HALIGN (gst_base_text_overlay_halign_get_type())
249 gst_base_text_overlay_halign_get_type (void)
251 static GType base_text_overlay_halign_type = 0;
252 static const GEnumValue base_text_overlay_halign[] = {
253 {GST_BASE_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"},
254 {GST_BASE_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"},
255 {GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"},
256 {GST_BASE_TEXT_OVERLAY_HALIGN_POS, "position", "position"},
260 if (!base_text_overlay_halign_type) {
261 base_text_overlay_halign_type =
262 g_enum_register_static ("GstBaseTextOverlayHAlign",
263 base_text_overlay_halign);
265 return base_text_overlay_halign_type;
269 #define GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE (gst_base_text_overlay_wrap_mode_get_type())
271 gst_base_text_overlay_wrap_mode_get_type (void)
273 static GType base_text_overlay_wrap_mode_type = 0;
274 static const GEnumValue base_text_overlay_wrap_mode[] = {
275 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"},
276 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"},
277 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"},
278 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"},
282 if (!base_text_overlay_wrap_mode_type) {
283 base_text_overlay_wrap_mode_type =
284 g_enum_register_static ("GstBaseTextOverlayWrapMode",
285 base_text_overlay_wrap_mode);
287 return base_text_overlay_wrap_mode_type;
290 #define GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN (gst_base_text_overlay_line_align_get_type())
292 gst_base_text_overlay_line_align_get_type (void)
294 static GType base_text_overlay_line_align_type = 0;
295 static const GEnumValue base_text_overlay_line_align[] = {
296 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"},
297 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"},
298 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"},
302 if (!base_text_overlay_line_align_type) {
303 base_text_overlay_line_align_type =
304 g_enum_register_static ("GstBaseTextOverlayLineAlign",
305 base_text_overlay_line_align);
307 return base_text_overlay_line_align_type;
310 #define GST_BASE_TEXT_OVERLAY_GET_COND(ov) (((GstBaseTextOverlay *)ov)->cond)
311 #define GST_BASE_TEXT_OVERLAY_WAIT(ov) (g_cond_wait (GST_BASE_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov)))
312 #define GST_BASE_TEXT_OVERLAY_SIGNAL(ov) (g_cond_signal (GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
313 #define GST_BASE_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
315 static GstElementClass *parent_class = NULL;
316 static void gst_base_text_overlay_base_init (gpointer g_class);
317 static void gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass);
318 static void gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
319 GstBaseTextOverlayClass * klass);
321 static GstStateChangeReturn gst_base_text_overlay_change_state (GstElement *
322 element, GstStateChange transition);
324 static GstCaps *gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter);
325 static gboolean gst_base_text_overlay_setcaps (GstPad * pad, GstCaps * caps);
326 static gboolean gst_base_text_overlay_setcaps_txt (GstPad * pad,
328 static gboolean gst_base_text_overlay_src_event (GstPad * pad,
330 static gboolean gst_base_text_overlay_src_query (GstPad * pad,
333 static gboolean gst_base_text_overlay_video_event (GstPad * pad,
335 static GstFlowReturn gst_base_text_overlay_video_chain (GstPad * pad,
338 static gboolean gst_base_text_overlay_text_event (GstPad * pad,
340 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
342 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
344 static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
345 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
346 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
349 static void gst_base_text_overlay_finalize (GObject * object);
350 static void gst_base_text_overlay_set_property (GObject * object, guint prop_id,
351 const GValue * value, GParamSpec * pspec);
352 static void gst_base_text_overlay_get_property (GObject * object, guint prop_id,
353 GValue * value, GParamSpec * pspec);
355 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
356 PangoFontDescription * desc);
359 gst_base_text_overlay_get_type (void)
361 static GType type = 0;
363 if (g_once_init_enter ((gsize *) & type)) {
364 static const GTypeInfo info = {
365 sizeof (GstBaseTextOverlayClass),
366 (GBaseInitFunc) gst_base_text_overlay_base_init,
368 (GClassInitFunc) gst_base_text_overlay_class_init,
371 sizeof (GstBaseTextOverlay),
373 (GInstanceInitFunc) gst_base_text_overlay_init,
376 g_once_init_leave ((gsize *) & type,
377 g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTextOverlay", &info,
385 gst_base_text_overlay_get_text (GstBaseTextOverlay * overlay,
386 GstBuffer * video_frame)
388 return g_strdup (overlay->default_text);
392 gst_base_text_overlay_base_init (gpointer g_class)
394 GstBaseTextOverlayClass *klass = GST_BASE_TEXT_OVERLAY_CLASS (g_class);
395 PangoFontMap *fontmap;
397 /* Only lock for the subclasses here, the base class
398 * doesn't have this mutex yet and it's not necessary
400 if (klass->pango_lock)
401 g_mutex_lock (klass->pango_lock);
402 fontmap = pango_cairo_font_map_get_default ();
403 klass->pango_context =
404 pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
405 if (klass->pango_lock)
406 g_mutex_unlock (klass->pango_lock);
410 gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
412 GObjectClass *gobject_class;
413 GstElementClass *gstelement_class;
415 gobject_class = (GObjectClass *) klass;
416 gstelement_class = (GstElementClass *) klass;
418 parent_class = g_type_class_peek_parent (klass);
420 gobject_class->finalize = gst_base_text_overlay_finalize;
421 gobject_class->set_property = gst_base_text_overlay_set_property;
422 gobject_class->get_property = gst_base_text_overlay_get_property;
424 gst_element_class_add_pad_template (gstelement_class,
425 gst_static_pad_template_get (&src_template_factory));
426 gst_element_class_add_pad_template (gstelement_class,
427 gst_static_pad_template_get (&video_sink_template_factory));
429 gstelement_class->change_state =
430 GST_DEBUG_FUNCPTR (gst_base_text_overlay_change_state);
432 klass->pango_lock = g_mutex_new ();
434 klass->get_text = gst_base_text_overlay_get_text;
436 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
437 g_param_spec_string ("text", "text",
438 "Text to be display.", DEFAULT_PROP_TEXT,
439 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
440 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
441 g_param_spec_boolean ("shaded-background", "shaded background",
442 "Whether to shade the background under the text area",
443 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
444 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
445 g_param_spec_enum ("valignment", "vertical alignment",
446 "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
447 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
448 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
449 g_param_spec_enum ("halignment", "horizontal alignment",
450 "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
451 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
452 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
453 g_param_spec_string ("valign", "vertical alignment",
454 "Vertical alignment of the text (deprecated; use valignment)",
455 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
456 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
457 g_param_spec_string ("halign", "horizontal alignment",
458 "Horizontal alignment of the text (deprecated; use halignment)",
459 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
460 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
461 g_param_spec_int ("xpad", "horizontal paddding",
462 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
463 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
464 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
465 g_param_spec_int ("ypad", "vertical padding",
466 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
467 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
468 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
469 g_param_spec_int ("deltax", "X position modifier",
470 "Shift X position to the left or to the right. Unit is pixels.",
471 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
472 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
473 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
474 g_param_spec_int ("deltay", "Y position modifier",
475 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
476 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
478 * GstBaseTextOverlay:xpos
480 * Horizontal position of the rendered text when using positioned alignment.
484 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
485 g_param_spec_double ("xpos", "horizontal position",
486 "Horizontal position when using position alignment", 0, 1.0,
488 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
490 * GstBaseTextOverlay:ypos
492 * Vertical position of the rendered text when using positioned alignment.
496 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
497 g_param_spec_double ("ypos", "vertical position",
498 "Vertical position when using position alignment", 0, 1.0,
500 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
501 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
502 g_param_spec_enum ("wrap-mode", "wrap mode",
503 "Whether to wrap the text and if so how.",
504 GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
505 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
506 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
507 g_param_spec_string ("font-desc", "font description",
508 "Pango font description of font to be used for rendering. "
509 "See documentation of pango_font_description_from_string "
510 "for syntax.", DEFAULT_PROP_FONT_DESC,
511 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
513 * GstBaseTextOverlay:color
515 * Color of the rendered text.
519 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
520 g_param_spec_uint ("color", "Color",
521 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
523 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
525 * GstTextOverlay:outline-color
527 * Color of the outline of the rendered text.
531 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
532 g_param_spec_uint ("outline-color", "Text Outline Color",
533 "Color to use for outline the text (big-endian ARGB).", 0,
534 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
535 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
538 * GstBaseTextOverlay:line-alignment
540 * Alignment of text lines relative to each other (for multi-line text)
544 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
545 g_param_spec_enum ("line-alignment", "line alignment",
546 "Alignment of text lines relative to each other.",
547 GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
548 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
550 * GstBaseTextOverlay:silent
552 * If set, no text is rendered. Useful to switch off text rendering
553 * temporarily without removing the textoverlay element from the pipeline.
557 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
558 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
559 g_param_spec_boolean ("silent", "silent",
560 "Whether to render the text string",
562 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
564 * GstBaseTextOverlay:wait-text
566 * If set, the video will block until a subtitle is received on the text pad.
567 * If video and subtitles are sent in sync, like from the same demuxer, this
568 * property should be set.
572 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
573 g_param_spec_boolean ("wait-text", "Wait Text",
574 "Whether to wait for subtitles",
575 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
577 g_object_class_install_property (G_OBJECT_CLASS (klass),
578 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
579 "Automatically adjust font size to screen-size.",
580 DEFAULT_PROP_AUTO_ADJUST_SIZE,
581 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
583 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
584 g_param_spec_boolean ("vertical-render", "vertical render",
585 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
586 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
590 gst_base_text_overlay_finalize (GObject * object)
592 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
594 g_free (overlay->default_text);
596 if (overlay->text_image) {
597 g_free (overlay->text_image);
598 overlay->text_image = NULL;
601 if (overlay->layout) {
602 g_object_unref (overlay->layout);
603 overlay->layout = NULL;
606 if (overlay->text_buffer) {
607 gst_buffer_unref (overlay->text_buffer);
608 overlay->text_buffer = NULL;
612 g_cond_free (overlay->cond);
613 overlay->cond = NULL;
616 G_OBJECT_CLASS (parent_class)->finalize (object);
620 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
621 GstBaseTextOverlayClass * klass)
623 GstPadTemplate *template;
624 PangoFontDescription *desc;
627 template = gst_static_pad_template_get (&video_sink_template_factory);
628 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
629 gst_object_unref (template);
630 gst_pad_set_getcaps_function (overlay->video_sinkpad,
631 GST_DEBUG_FUNCPTR (gst_base_text_overlay_getcaps));
632 gst_pad_set_event_function (overlay->video_sinkpad,
633 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_event));
634 gst_pad_set_chain_function (overlay->video_sinkpad,
635 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
636 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
639 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
643 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
644 gst_object_unref (template);
646 gst_pad_set_event_function (overlay->text_sinkpad,
647 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
648 gst_pad_set_chain_function (overlay->text_sinkpad,
649 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
650 gst_pad_set_link_function (overlay->text_sinkpad,
651 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
652 gst_pad_set_unlink_function (overlay->text_sinkpad,
653 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
654 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
658 template = gst_static_pad_template_get (&src_template_factory);
659 overlay->srcpad = gst_pad_new_from_template (template, "src");
660 gst_object_unref (template);
661 gst_pad_set_getcaps_function (overlay->srcpad,
662 GST_DEBUG_FUNCPTR (gst_base_text_overlay_getcaps));
663 gst_pad_set_event_function (overlay->srcpad,
664 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_event));
665 gst_pad_set_query_function (overlay->srcpad,
666 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_query));
667 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
669 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
670 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
672 pango_layout_new (GST_BASE_TEXT_OVERLAY_GET_CLASS
673 (overlay)->pango_context);
675 pango_context_get_font_description (GST_BASE_TEXT_OVERLAY_GET_CLASS
676 (overlay)->pango_context);
677 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
679 overlay->color = DEFAULT_PROP_COLOR;
680 overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
681 overlay->halign = DEFAULT_PROP_HALIGNMENT;
682 overlay->valign = DEFAULT_PROP_VALIGNMENT;
683 overlay->xpad = DEFAULT_PROP_XPAD;
684 overlay->ypad = DEFAULT_PROP_YPAD;
685 overlay->deltax = DEFAULT_PROP_DELTAX;
686 overlay->deltay = DEFAULT_PROP_DELTAY;
687 overlay->xpos = DEFAULT_PROP_XPOS;
688 overlay->ypos = DEFAULT_PROP_YPOS;
690 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
692 overlay->want_shading = DEFAULT_PROP_SHADING;
693 overlay->shading_value = DEFAULT_SHADING_VALUE;
694 overlay->silent = DEFAULT_PROP_SILENT;
695 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
696 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
698 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
699 overlay->need_render = TRUE;
700 overlay->text_image = NULL;
701 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
702 gst_base_text_overlay_update_render_mode (overlay);
707 overlay->text_buffer = NULL;
708 overlay->text_linked = FALSE;
709 overlay->cond = g_cond_new ();
710 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
711 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
715 gst_base_text_overlay_update_wrap_mode (GstBaseTextOverlay * overlay)
717 if (overlay->wrap_mode == GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE) {
718 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
719 pango_layout_set_width (overlay->layout, -1);
723 if (overlay->auto_adjust_size) {
724 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
725 if (overlay->use_vertical_render) {
726 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
730 (overlay->use_vertical_render ? overlay->height : overlay->width) *
734 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
735 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
736 pango_layout_set_width (overlay->layout, width);
737 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
742 gst_base_text_overlay_update_render_mode (GstBaseTextOverlay * overlay)
744 PangoMatrix matrix = PANGO_MATRIX_INIT;
745 PangoContext *context = pango_layout_get_context (overlay->layout);
747 if (overlay->use_vertical_render) {
748 pango_matrix_rotate (&matrix, -90);
749 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
750 pango_context_set_matrix (context, &matrix);
751 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
753 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
754 pango_context_set_matrix (context, &matrix);
755 pango_layout_set_alignment (overlay->layout, overlay->line_align);
760 gst_base_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps)
762 GstBaseTextOverlay *overlay;
763 GstStructure *structure;
765 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
767 structure = gst_caps_get_structure (caps, 0);
768 overlay->have_pango_markup =
769 gst_structure_has_name (structure, "text/x-pango-markup");
771 gst_object_unref (overlay);
776 /* FIXME: upstream nego (e.g. when the video window is resized) */
779 gst_base_text_overlay_setcaps (GstPad * pad, GstCaps * caps)
781 GstBaseTextOverlay *overlay;
782 GstStructure *structure;
783 gboolean ret = FALSE;
786 if (!GST_PAD_IS_SINK (pad))
789 g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
791 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
795 structure = gst_caps_get_structure (caps, 0);
796 fps = gst_structure_get_value (structure, "framerate");
799 && gst_video_format_parse_caps (caps, &overlay->format, &overlay->width,
801 ret = gst_pad_push_event (overlay->srcpad, gst_event_new_caps (caps));
804 overlay->fps_n = gst_value_get_fraction_numerator (fps);
805 overlay->fps_d = gst_value_get_fraction_denominator (fps);
808 GST_OBJECT_LOCK (overlay);
809 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
810 gst_base_text_overlay_update_wrap_mode (overlay);
811 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
812 GST_OBJECT_UNLOCK (overlay);
815 gst_object_unref (overlay);
821 gst_base_text_overlay_set_property (GObject * object, guint prop_id,
822 const GValue * value, GParamSpec * pspec)
824 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
826 GST_OBJECT_LOCK (overlay);
829 g_free (overlay->default_text);
830 overlay->default_text = g_value_dup_string (value);
831 overlay->need_render = TRUE;
834 overlay->want_shading = g_value_get_boolean (value);
837 overlay->xpad = g_value_get_int (value);
840 overlay->ypad = g_value_get_int (value);
843 overlay->deltax = g_value_get_int (value);
846 overlay->deltay = g_value_get_int (value);
849 overlay->xpos = g_value_get_double (value);
852 overlay->ypos = g_value_get_double (value);
855 const gchar *s = g_value_get_string (value);
857 if (s && g_ascii_strcasecmp (s, "left") == 0)
858 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_LEFT;
859 else if (s && g_ascii_strcasecmp (s, "center") == 0)
860 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_CENTER;
861 else if (s && g_ascii_strcasecmp (s, "right") == 0)
862 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
864 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
869 const gchar *s = g_value_get_string (value);
871 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
872 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE;
873 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
874 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM;
875 else if (s && g_ascii_strcasecmp (s, "top") == 0)
876 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
878 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
882 case PROP_VALIGNMENT:
883 overlay->valign = g_value_get_enum (value);
885 case PROP_HALIGNMENT:
886 overlay->halign = g_value_get_enum (value);
889 overlay->wrap_mode = g_value_get_enum (value);
890 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
891 gst_base_text_overlay_update_wrap_mode (overlay);
892 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
896 PangoFontDescription *desc;
897 const gchar *fontdesc_str;
899 fontdesc_str = g_value_get_string (value);
900 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
901 desc = pango_font_description_from_string (fontdesc_str);
903 GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
904 pango_layout_set_font_description (overlay->layout, desc);
905 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
906 pango_font_description_free (desc);
908 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
911 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
915 overlay->color = g_value_get_uint (value);
917 case PROP_OUTLINE_COLOR:
918 overlay->outline_color = g_value_get_uint (value);
921 overlay->silent = g_value_get_boolean (value);
923 case PROP_LINE_ALIGNMENT:
924 overlay->line_align = g_value_get_enum (value);
925 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
926 pango_layout_set_alignment (overlay->layout,
927 (PangoAlignment) overlay->line_align);
928 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
931 overlay->wait_text = g_value_get_boolean (value);
933 case PROP_AUTO_ADJUST_SIZE:
934 overlay->auto_adjust_size = g_value_get_boolean (value);
935 overlay->need_render = TRUE;
937 case PROP_VERTICAL_RENDER:
938 overlay->use_vertical_render = g_value_get_boolean (value);
939 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
940 gst_base_text_overlay_update_render_mode (overlay);
941 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
942 overlay->need_render = TRUE;
945 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
949 overlay->need_render = TRUE;
950 GST_OBJECT_UNLOCK (overlay);
954 gst_base_text_overlay_get_property (GObject * object, guint prop_id,
955 GValue * value, GParamSpec * pspec)
957 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
959 GST_OBJECT_LOCK (overlay);
962 g_value_set_string (value, overlay->default_text);
965 g_value_set_boolean (value, overlay->want_shading);
968 g_value_set_int (value, overlay->xpad);
971 g_value_set_int (value, overlay->ypad);
974 g_value_set_int (value, overlay->deltax);
977 g_value_set_int (value, overlay->deltay);
980 g_value_set_double (value, overlay->xpos);
983 g_value_set_double (value, overlay->ypos);
985 case PROP_VALIGNMENT:
986 g_value_set_enum (value, overlay->valign);
988 case PROP_HALIGNMENT:
989 g_value_set_enum (value, overlay->halign);
992 g_value_set_enum (value, overlay->wrap_mode);
995 g_value_set_boolean (value, overlay->silent);
997 case PROP_LINE_ALIGNMENT:
998 g_value_set_enum (value, overlay->line_align);
1000 case PROP_WAIT_TEXT:
1001 g_value_set_boolean (value, overlay->wait_text);
1003 case PROP_AUTO_ADJUST_SIZE:
1004 g_value_set_boolean (value, overlay->auto_adjust_size);
1006 case PROP_VERTICAL_RENDER:
1007 g_value_set_boolean (value, overlay->use_vertical_render);
1010 g_value_set_uint (value, overlay->color);
1012 case PROP_OUTLINE_COLOR:
1013 g_value_set_uint (value, overlay->outline_color);
1016 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1020 overlay->need_render = TRUE;
1021 GST_OBJECT_UNLOCK (overlay);
1025 gst_base_text_overlay_src_query (GstPad * pad, GstQuery * query)
1027 gboolean ret = FALSE;
1028 GstBaseTextOverlay *overlay = NULL;
1030 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1032 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1034 gst_object_unref (overlay);
1040 gst_base_text_overlay_src_event (GstPad * pad, GstEvent * event)
1042 gboolean ret = FALSE;
1043 GstBaseTextOverlay *overlay = NULL;
1045 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1047 switch (GST_EVENT_TYPE (event)) {
1048 case GST_EVENT_SEEK:{
1051 /* We don't handle seek if we have not text pad */
1052 if (!overlay->text_linked) {
1053 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1054 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1058 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1060 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1062 /* Flush downstream, only for flushing seek */
1063 if (flags & GST_SEEK_FLAG_FLUSH)
1064 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1066 /* Mark ourself as flushing, unblock chains */
1067 GST_OBJECT_LOCK (overlay);
1068 overlay->video_flushing = TRUE;
1069 overlay->text_flushing = TRUE;
1070 gst_base_text_overlay_pop_text (overlay);
1071 GST_OBJECT_UNLOCK (overlay);
1073 /* Seek on each sink pad */
1074 gst_event_ref (event);
1075 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1077 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1079 gst_event_unref (event);
1084 if (overlay->text_linked) {
1085 gst_event_ref (event);
1086 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1087 gst_pad_push_event (overlay->text_sinkpad, event);
1089 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1095 gst_object_unref (overlay);
1101 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1103 GstBaseTextOverlay *overlay;
1107 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1109 if (pad == overlay->srcpad)
1110 otherpad = overlay->video_sinkpad;
1112 otherpad = overlay->srcpad;
1114 /* we can do what the peer can */
1115 caps = gst_pad_peer_get_caps (otherpad, filter);
1117 GstCaps *temp, *templ;
1119 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1121 /* filtered against our padtemplate */
1122 templ = gst_pad_get_pad_template_caps (otherpad);
1123 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1124 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1125 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1126 gst_caps_unref (caps);
1127 gst_caps_unref (templ);
1128 /* this is what we can do */
1131 /* no peer, our padtemplate is enough then */
1132 caps = gst_pad_get_pad_template_caps (pad);
1134 GstCaps *intersection;
1137 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1138 gst_caps_unref (caps);
1139 caps = intersection;
1143 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1145 gst_object_unref (overlay);
1151 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1152 PangoFontDescription * desc)
1154 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1155 overlay->shadow_offset = (double) (font_size) / 13.0;
1156 overlay->outline_offset = (double) (font_size) / 15.0;
1157 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1158 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1161 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1162 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1163 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1164 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1168 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1169 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1176 gint width = overlay->image_width;
1177 gint height = overlay->image_height;
1183 if (xpos + width > overlay->width) {
1184 width = overlay->width - xpos;
1187 if (ypos + height > overlay->height) {
1188 height = overlay->height - ypos;
1191 dest += (ypos / 1) * dest_stride;
1193 for (i = 0; i < height; i++) {
1194 pimage = text_image + 4 * (i * overlay->image_width);
1195 py = dest + i * dest_stride + xpos;
1196 for (j = 0; j < width; j++) {
1197 b = pimage[CAIRO_ARGB_B];
1198 g = pimage[CAIRO_ARGB_G];
1199 r = pimage[CAIRO_ARGB_R];
1200 a = pimage[CAIRO_ARGB_A];
1201 CAIRO_UNPREMULTIPLY (a, r, g, b);
1208 COMP_Y (y, r, g, b);
1210 BLEND (*py++, a, y, x);
1216 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1217 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1218 guint destcb_stride, guint destcr_stride, guint pix_stride)
1223 gushort r1, g1, b1, a1;
1224 guchar *pimage1, *pimage2;
1226 gint width = overlay->image_width - 2;
1227 gint height = overlay->image_height - 2;
1235 if (xpos + width > overlay->width) {
1236 width = overlay->width - xpos;
1239 if (ypos + height > overlay->height) {
1240 height = overlay->height - ypos;
1243 destcb += (ypos / 2) * destcb_stride;
1244 destcr += (ypos / 2) * destcr_stride;
1246 for (i = 0; i < height; i += 2) {
1247 pimage1 = text_image + 4 * (i * overlay->image_width);
1248 pimage2 = pimage1 + 4 * overlay->image_width;
1249 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1250 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1251 for (j = 0; j < width; j += 2) {
1252 b = pimage1[CAIRO_ARGB_B];
1253 g = pimage1[CAIRO_ARGB_G];
1254 r = pimage1[CAIRO_ARGB_R];
1255 a = pimage1[CAIRO_ARGB_A];
1256 CAIRO_UNPREMULTIPLY (a, r, g, b);
1259 b1 = pimage1[CAIRO_ARGB_B];
1260 g1 = pimage1[CAIRO_ARGB_G];
1261 r1 = pimage1[CAIRO_ARGB_R];
1262 a1 = pimage1[CAIRO_ARGB_A];
1263 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1270 b1 = pimage2[CAIRO_ARGB_B];
1271 g1 = pimage2[CAIRO_ARGB_G];
1272 r1 = pimage2[CAIRO_ARGB_R];
1273 a1 = pimage2[CAIRO_ARGB_A];
1274 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1281 /* + 2 for rounding */
1282 b1 = pimage2[CAIRO_ARGB_B];
1283 g1 = pimage2[CAIRO_ARGB_G];
1284 r1 = pimage2[CAIRO_ARGB_R];
1285 a1 = pimage2[CAIRO_ARGB_A];
1286 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1303 COMP_U (cb, r, g, b);
1304 COMP_V (cr, r, g, b);
1307 BLEND (*pcb, a, cb, x);
1309 BLEND (*pcr, a, cr, x);
1318 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1319 const gchar * string, gint textlen)
1322 cairo_surface_t *surface;
1323 PangoRectangle ink_rect, logical_rect;
1324 cairo_matrix_t cairo_matrix;
1326 double scalef = 1.0;
1329 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1331 if (overlay->auto_adjust_size) {
1332 /* 640 pixel is default */
1333 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1335 pango_layout_set_width (overlay->layout, -1);
1336 /* set text on pango layout */
1337 pango_layout_set_markup (overlay->layout, string, textlen);
1339 /* get subtitle image size */
1340 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1342 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1344 if (width + overlay->deltax >
1345 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1347 * subtitle image width is larger then overlay width
1348 * so rearrange overlay wrap mode.
1350 gst_base_text_overlay_update_wrap_mode (overlay);
1351 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1352 width = overlay->width;
1356 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1357 if (height > overlay->height) {
1358 height = overlay->height;
1360 if (overlay->use_vertical_render) {
1361 PangoRectangle rect;
1362 PangoContext *context;
1363 PangoMatrix matrix = PANGO_MATRIX_INIT;
1366 context = pango_layout_get_context (overlay->layout);
1368 pango_matrix_rotate (&matrix, -90);
1370 rect.x = rect.y = 0;
1372 rect.height = height;
1373 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1374 matrix.x0 = -rect.x;
1375 matrix.y0 = -rect.y;
1377 pango_context_set_matrix (context, &matrix);
1379 cairo_matrix.xx = matrix.xx;
1380 cairo_matrix.yx = matrix.yx;
1381 cairo_matrix.xy = matrix.xy;
1382 cairo_matrix.yy = matrix.yy;
1383 cairo_matrix.x0 = matrix.x0;
1384 cairo_matrix.y0 = matrix.y0;
1385 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1391 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1394 /* reallocate surface */
1395 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1397 surface = cairo_image_surface_create_for_data (overlay->text_image,
1398 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1399 cr = cairo_create (surface);
1402 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1405 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1407 if (overlay->want_shading)
1408 cairo_paint_with_alpha (cr, overlay->shading_value);
1410 /* apply transformations */
1411 cairo_set_matrix (cr, &cairo_matrix);
1413 /* FIXME: We use show_layout everywhere except for the surface
1414 * because it's really faster and internally does all kinds of
1415 * caching. Unfortunately we have to paint to a cairo path for
1416 * the outline and this is slow. Once Pango supports user fonts
1417 * we should use them, see
1418 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1420 * Idea would the be, to create a cairo user font that
1421 * does shadow, outline, text painting in the
1422 * render_glyph function.
1425 /* draw shadow text */
1427 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1428 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1429 pango_cairo_show_layout (cr, overlay->layout);
1432 a = (overlay->outline_color >> 24) & 0xff;
1433 r = (overlay->outline_color >> 16) & 0xff;
1434 g = (overlay->outline_color >> 8) & 0xff;
1435 b = (overlay->outline_color >> 0) & 0xff;
1437 /* draw outline text */
1439 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1440 cairo_set_line_width (cr, overlay->outline_offset);
1441 pango_cairo_layout_path (cr, overlay->layout);
1445 a = (overlay->color >> 24) & 0xff;
1446 r = (overlay->color >> 16) & 0xff;
1447 g = (overlay->color >> 8) & 0xff;
1448 b = (overlay->color >> 0) & 0xff;
1452 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1453 pango_cairo_show_layout (cr, overlay->layout);
1457 cairo_surface_destroy (surface);
1458 overlay->image_width = width;
1459 overlay->image_height = height;
1460 overlay->baseline_y = ink_rect.y;
1461 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1468 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1469 guchar * dest, gint x0, gint x1, gint y0, gint y1)
1471 gint i, j, dest_stride;
1473 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1476 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1477 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1479 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1480 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1482 for (i = y0; i < y1; ++i) {
1483 for (j = x0; j < x1; ++j) {
1484 gint y = dest[(i * dest_stride) + j] + overlay->shading_value;
1486 dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1492 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1493 guchar * dest, gint x0, gint x1, gint y0, gint y1)
1496 guint dest_stride, pixel_stride, component_offset;
1498 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1500 pixel_stride = gst_video_format_get_pixel_stride (overlay->format, 0);
1502 gst_video_format_get_component_offset (overlay->format, 0, overlay->width,
1505 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1506 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1508 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1509 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1512 x0 = gst_video_format_get_component_width (overlay->format, 0, x0);
1514 x1 = gst_video_format_get_component_width (overlay->format, 0, x1);
1517 y0 = gst_video_format_get_component_height (overlay->format, 0, y0);
1519 y1 = gst_video_format_get_component_height (overlay->format, 0, y1);
1521 for (i = y0; i < y1; i++) {
1522 for (j = x0; j < x1; j++) {
1526 y_pos = (i * dest_stride) + j * pixel_stride + component_offset;
1527 y = dest[y_pos] + overlay->shading_value;
1529 dest[y_pos] = CLAMP (y, 0, 255);
1534 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1535 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1536 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1538 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay, guchar * dest,
1539 gint x0, gint x1, gint y0, gint y1)
1543 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1544 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1546 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1547 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1549 for (i = y0; i < y1; i++) {
1550 for (j = x0; j < x1; j++) {
1553 y_pos = (i * 4 * overlay->width) + j * 4;
1554 for (k = 0; k < 4; k++) {
1555 y = dest[y_pos + k] + overlay->shading_value;
1556 dest[y_pos + k] = CLAMP (y, 0, 255);
1562 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1563 static inline void \
1564 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, guchar * dest, \
1565 gint x0, gint x1, gint y0, gint y1) \
1569 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1570 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1572 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1573 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1575 for (i = y0; i < y1; i++) {\
1576 for (j = x0; j < x1; j++) {\
1578 y_pos = (i * 4 * overlay->width) + j * 4;\
1579 for (k = OFFSET; k < 3+OFFSET; k++) {\
1580 y = dest[y_pos + k] + overlay->shading_value;\
1581 dest[y_pos + k] = CLAMP (y, 0, 255);\
1586 ARGB_SHADE_FUNCTION (ARGB, 1);
1587 ARGB_SHADE_FUNCTION (ABGR, 1);
1588 ARGB_SHADE_FUNCTION (RGBA, 0);
1589 ARGB_SHADE_FUNCTION (BGRA, 0);
1593 * - use proper strides and offset for I420
1594 * - don't draw over the edge of the picture (try a longer
1595 * text with a huge font size)
1599 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1600 guint8 * yuv_pixels, gint xpos, gint ypos)
1602 int y_stride, uv_stride;
1603 int u_offset, v_offset;
1606 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1607 * to a boundary of integer number of U/V pixels:
1609 xpos = GST_ROUND_UP_2 (xpos);
1610 ypos = GST_ROUND_UP_2 (ypos);
1613 h = overlay->height;
1615 y_stride = gst_video_format_get_row_stride (overlay->format, 0, w);
1616 uv_stride = gst_video_format_get_row_stride (overlay->format, 1, w);
1617 u_offset = gst_video_format_get_component_offset (overlay->format, 1, w, h);
1618 v_offset = gst_video_format_get_component_offset (overlay->format, 2, w, h);
1620 gst_base_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos,
1621 overlay->text_image, y_stride);
1622 gst_base_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1623 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, uv_stride,
1628 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1629 guint8 * yuv_pixels, gint xpos, gint ypos)
1631 int y_stride, u_stride, v_stride;
1632 int u_offset, v_offset;
1635 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1636 * to a boundary of integer number of U/V pixels:
1638 xpos = GST_ROUND_UP_2 (xpos);
1639 ypos = GST_ROUND_UP_2 (ypos);
1642 h = overlay->height;
1644 y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, w);
1645 u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, w);
1646 v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, w);
1648 gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1, w, h);
1650 gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h);
1652 gst_base_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos,
1653 overlay->text_image, y_stride);
1654 gst_base_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1655 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride,
1660 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1661 guint8 * yuv_pixels, gint xpos, gint ypos)
1668 guchar *pimage, *dest;
1670 /* because U/V is 2x horizontally subsampled, we need to round to a
1671 * boundary of integer number of U/V pixels in x dimension:
1673 xpos = GST_ROUND_UP_2 (xpos);
1675 w = overlay->image_width - 2;
1676 h = overlay->image_height - 2;
1682 if (xpos + w > overlay->width) {
1683 w = overlay->width - xpos;
1686 if (ypos + h > overlay->height) {
1687 h = overlay->height - ypos;
1690 for (i = 0; i < h; i++) {
1691 pimage = overlay->text_image + i * overlay->image_width * 4;
1692 dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1693 for (j = 0; j < w; j += 2) {
1694 b0 = pimage[CAIRO_ARGB_B];
1695 g0 = pimage[CAIRO_ARGB_G];
1696 r0 = pimage[CAIRO_ARGB_R];
1697 a0 = pimage[CAIRO_ARGB_A];
1698 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1701 b1 = pimage[CAIRO_ARGB_B];
1702 g1 = pimage[CAIRO_ARGB_G];
1703 r1 = pimage[CAIRO_ARGB_R];
1704 a1 = pimage[CAIRO_ARGB_A];
1705 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1715 COMP_Y (y0, r0, g0, b0);
1716 COMP_Y (y1, r1, g1, b1);
1726 COMP_U (u, r0, g0, b0);
1727 COMP_V (v, r0, g0, b0);
1729 BLEND (*dest, a0, u, *dest);
1731 BLEND (*dest, a0, y0, *dest);
1733 BLEND (*dest, a0, v, *dest);
1735 BLEND (*dest, a0, y1, *dest);
1742 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1743 guint8 * rgb_pixels, gint xpos, gint ypos)
1749 guchar *pimage, *dest;
1751 w = overlay->image_width;
1752 h = overlay->image_height;
1758 if (xpos + w > overlay->width) {
1759 w = overlay->width - xpos;
1762 if (ypos + h > overlay->height) {
1763 h = overlay->height - ypos;
1766 for (i = 0; i < h; i++) {
1767 pimage = overlay->text_image + i * overlay->image_width * 4;
1768 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1769 for (j = 0; j < w; j++) {
1770 a = pimage[CAIRO_ARGB_A];
1771 b = pimage[CAIRO_ARGB_B];
1772 g = pimage[CAIRO_ARGB_G];
1773 r = pimage[CAIRO_ARGB_R];
1775 CAIRO_UNPREMULTIPLY (a, r, g, b);
1777 // convert background to yuv
1778 COMP_Y (y, r, g, b);
1779 COMP_U (u, r, g, b);
1780 COMP_V (v, r, g, b);
1782 // preform text "OVER" background alpha compositing
1783 a1 = a + (dest[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1784 OVER (dest[1], a, y, dest[0], dest[1], a1);
1785 OVER (dest[2], a, u, dest[0], dest[2], a1);
1786 OVER (dest[3], a, v, dest[0], dest[3], a1);
1787 dest[0] = a1 - 1; // remove the temporary 1 we added
1795 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1796 static inline void \
1797 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1798 guint8 * rgb_pixels, gint xpos, gint ypos) \
1803 guchar *pimage, *dest; \
1805 w = overlay->image_width; \
1806 h = overlay->image_height; \
1812 if (xpos + w > overlay->width) { \
1813 w = overlay->width - xpos; \
1816 if (ypos + h > overlay->height) { \
1817 h = overlay->height - ypos; \
1820 for (i = 0; i < h; i++) { \
1821 pimage = overlay->text_image + i * overlay->image_width * 4; \
1822 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1823 for (j = 0; j < w; j++) { \
1824 a = pimage[CAIRO_ARGB_A]; \
1825 b = pimage[CAIRO_ARGB_B]; \
1826 g = pimage[CAIRO_ARGB_G]; \
1827 r = pimage[CAIRO_ARGB_R]; \
1828 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1829 b = (b*a + dest[B] * (255-a)) / 255; \
1830 g = (g*a + dest[G] * (255-a)) / 255; \
1831 r = (r*a + dest[R] * (255-a)) / 255; \
1841 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1842 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1843 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1844 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1846 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1847 static inline void \
1848 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1849 guint8 * rgb_pixels, gint xpos, gint ypos) \
1851 int a, r, g, b, a1; \
1854 guchar *pimage, *dest; \
1856 w = overlay->image_width; \
1857 h = overlay->image_height; \
1863 if (xpos + w > overlay->width) { \
1864 w = overlay->width - xpos; \
1867 if (ypos + h > overlay->height) { \
1868 h = overlay->height - ypos; \
1871 for (i = 0; i < h; i++) { \
1872 pimage = overlay->text_image + i * overlay->image_width * 4; \
1873 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1874 for (j = 0; j < w; j++) { \
1875 a = pimage[CAIRO_ARGB_A]; \
1876 b = pimage[CAIRO_ARGB_B]; \
1877 g = pimage[CAIRO_ARGB_G]; \
1878 r = pimage[CAIRO_ARGB_R]; \
1879 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1880 a1 = a + (dest[A] * (255 - a)) / 255 + 1; \
1881 OVER (dest[R], a, r, dest[0], dest[R], a1); \
1882 OVER (dest[G], a, g, dest[0], dest[G], a1); \
1883 OVER (dest[B], a, b, dest[0], dest[B], a1); \
1890 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1891 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1892 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1893 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1896 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1897 const gchar * text, gint textlen)
1901 if (!overlay->need_render) {
1902 GST_DEBUG ("Using previously rendered text.");
1906 /* -1 is the whole string */
1907 if (text != NULL && textlen < 0) {
1908 textlen = strlen (text);
1912 string = g_strndup (text, textlen);
1913 } else { /* empty string */
1914 string = g_strdup (" ");
1916 g_strdelimit (string, "\r\t", ' ');
1917 textlen = strlen (string);
1919 /* FIXME: should we check for UTF-8 here? */
1921 GST_DEBUG ("Rendering '%s'", string);
1922 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1926 overlay->need_render = FALSE;
1929 static GstFlowReturn
1930 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1931 GstBuffer * video_frame)
1935 GstBaseTextOverlayVAlign valign;
1936 GstBaseTextOverlayHAlign halign;
1940 width = overlay->image_width;
1941 height = overlay->image_height;
1943 video_frame = gst_buffer_make_writable (video_frame);
1945 data = gst_buffer_map (video_frame, &size, NULL, GST_MAP_WRITE);
1947 if (overlay->use_vertical_render)
1948 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1950 halign = overlay->halign;
1953 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1954 xpos = overlay->xpad;
1956 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1957 xpos = (overlay->width - width) / 2;
1959 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1960 xpos = overlay->width - width - overlay->xpad;
1962 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1963 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1964 xpos = CLAMP (xpos, 0, overlay->width - width);
1971 xpos += overlay->deltax;
1973 if (overlay->use_vertical_render)
1974 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1976 valign = overlay->valign;
1979 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1980 ypos = overlay->height - height - overlay->ypad;
1982 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1983 ypos = overlay->height - (height + overlay->ypad);
1985 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1986 ypos = overlay->ypad;
1988 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1989 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1990 ypos = CLAMP (ypos, 0, overlay->height - height);
1992 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1993 ypos = (overlay->height - height) / 2;
1996 ypos = overlay->ypad;
1999 ypos += overlay->deltay;
2001 /* shaded background box */
2002 if (overlay->want_shading) {
2003 switch (overlay->format) {
2004 case GST_VIDEO_FORMAT_I420:
2005 case GST_VIDEO_FORMAT_NV12:
2006 case GST_VIDEO_FORMAT_NV21:
2007 gst_base_text_overlay_shade_planar_Y (overlay, data,
2008 xpos, xpos + overlay->image_width,
2009 ypos, ypos + overlay->image_height);
2011 case GST_VIDEO_FORMAT_AYUV:
2012 case GST_VIDEO_FORMAT_UYVY:
2013 gst_base_text_overlay_shade_packed_Y (overlay, data,
2014 xpos, xpos + overlay->image_width,
2015 ypos, ypos + overlay->image_height);
2017 case GST_VIDEO_FORMAT_xRGB:
2018 gst_base_text_overlay_shade_xRGB (overlay, data,
2019 xpos, xpos + overlay->image_width,
2020 ypos, ypos + overlay->image_height);
2022 case GST_VIDEO_FORMAT_xBGR:
2023 gst_base_text_overlay_shade_xBGR (overlay, data,
2024 xpos, xpos + overlay->image_width,
2025 ypos, ypos + overlay->image_height);
2027 case GST_VIDEO_FORMAT_BGRx:
2028 gst_base_text_overlay_shade_BGRx (overlay, data,
2029 xpos, xpos + overlay->image_width,
2030 ypos, ypos + overlay->image_height);
2032 case GST_VIDEO_FORMAT_RGBx:
2033 gst_base_text_overlay_shade_RGBx (overlay, data,
2034 xpos, xpos + overlay->image_width,
2035 ypos, ypos + overlay->image_height);
2037 case GST_VIDEO_FORMAT_ARGB:
2038 gst_base_text_overlay_shade_ARGB (overlay, data,
2039 xpos, xpos + overlay->image_width,
2040 ypos, ypos + overlay->image_height);
2042 case GST_VIDEO_FORMAT_ABGR:
2043 gst_base_text_overlay_shade_ABGR (overlay, data,
2044 xpos, xpos + overlay->image_width,
2045 ypos, ypos + overlay->image_height);
2047 case GST_VIDEO_FORMAT_RGBA:
2048 gst_base_text_overlay_shade_RGBA (overlay, data,
2049 xpos, xpos + overlay->image_width,
2050 ypos, ypos + overlay->image_height);
2052 case GST_VIDEO_FORMAT_BGRA:
2053 gst_base_text_overlay_shade_BGRA (overlay, data,
2054 xpos, xpos + overlay->image_width,
2055 ypos, ypos + overlay->image_height);
2058 g_assert_not_reached ();
2065 if (overlay->text_image) {
2066 switch (overlay->format) {
2067 case GST_VIDEO_FORMAT_I420:
2068 gst_base_text_overlay_blit_I420 (overlay, data, xpos, ypos);
2070 case GST_VIDEO_FORMAT_NV12:
2071 case GST_VIDEO_FORMAT_NV21:
2072 gst_base_text_overlay_blit_NV12_NV21 (overlay, data, xpos, ypos);
2074 case GST_VIDEO_FORMAT_UYVY:
2075 gst_base_text_overlay_blit_UYVY (overlay, data, xpos, ypos);
2077 case GST_VIDEO_FORMAT_AYUV:
2078 gst_base_text_overlay_blit_AYUV (overlay, data, xpos, ypos);
2080 case GST_VIDEO_FORMAT_BGRx:
2081 gst_base_text_overlay_blit_BGRx (overlay, data, xpos, ypos);
2083 case GST_VIDEO_FORMAT_xRGB:
2084 gst_base_text_overlay_blit_xRGB (overlay, data, xpos, ypos);
2086 case GST_VIDEO_FORMAT_RGBx:
2087 gst_base_text_overlay_blit_RGBx (overlay, data, xpos, ypos);
2089 case GST_VIDEO_FORMAT_xBGR:
2090 gst_base_text_overlay_blit_xBGR (overlay, data, xpos, ypos);
2092 case GST_VIDEO_FORMAT_ARGB:
2093 gst_base_text_overlay_blit_ARGB (overlay, data, xpos, ypos);
2095 case GST_VIDEO_FORMAT_ABGR:
2096 gst_base_text_overlay_blit_ABGR (overlay, data, xpos, ypos);
2098 case GST_VIDEO_FORMAT_RGBA:
2099 gst_base_text_overlay_blit_RGBA (overlay, data, xpos, ypos);
2101 case GST_VIDEO_FORMAT_BGRA:
2102 gst_base_text_overlay_blit_BGRA (overlay, data, xpos, ypos);
2105 g_assert_not_reached ();
2108 gst_buffer_unmap (video_frame, data, size);
2110 return gst_pad_push (overlay->srcpad, video_frame);
2113 static GstPadLinkReturn
2114 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2116 GstBaseTextOverlay *overlay;
2118 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2120 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2122 overlay->text_linked = TRUE;
2124 gst_object_unref (overlay);
2126 return GST_PAD_LINK_OK;
2130 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2132 GstBaseTextOverlay *overlay;
2134 /* don't use gst_pad_get_parent() here, will deadlock */
2135 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2137 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2139 overlay->text_linked = FALSE;
2141 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2145 gst_base_text_overlay_text_event (GstPad * pad, GstEvent * event)
2147 gboolean ret = FALSE;
2148 GstBaseTextOverlay *overlay = NULL;
2150 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2152 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2154 switch (GST_EVENT_TYPE (event)) {
2155 case GST_EVENT_CAPS:
2159 gst_event_parse_caps (event, &caps);
2160 ret = gst_base_text_overlay_setcaps_txt (pad, caps);
2161 gst_event_unref (event);
2164 case GST_EVENT_SEGMENT:
2166 const GstSegment *segment;
2168 overlay->text_eos = FALSE;
2170 gst_event_parse_segment (event, &segment);
2172 if (segment->format == GST_FORMAT_TIME) {
2173 GST_OBJECT_LOCK (overlay);
2174 gst_segment_copy_into (segment, &overlay->text_segment);
2175 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2176 &overlay->text_segment);
2177 GST_OBJECT_UNLOCK (overlay);
2179 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2180 ("received non-TIME newsegment event on text input"));
2183 gst_event_unref (event);
2186 /* wake up the video chain, it might be waiting for a text buffer or
2187 * a text segment update */
2188 GST_OBJECT_LOCK (overlay);
2189 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2190 GST_OBJECT_UNLOCK (overlay);
2193 case GST_EVENT_FLUSH_STOP:
2194 GST_OBJECT_LOCK (overlay);
2195 GST_INFO_OBJECT (overlay, "text flush stop");
2196 overlay->text_flushing = FALSE;
2197 overlay->text_eos = FALSE;
2198 gst_base_text_overlay_pop_text (overlay);
2199 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2200 GST_OBJECT_UNLOCK (overlay);
2201 gst_event_unref (event);
2204 case GST_EVENT_FLUSH_START:
2205 GST_OBJECT_LOCK (overlay);
2206 GST_INFO_OBJECT (overlay, "text flush start");
2207 overlay->text_flushing = TRUE;
2208 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2209 GST_OBJECT_UNLOCK (overlay);
2210 gst_event_unref (event);
2214 GST_OBJECT_LOCK (overlay);
2215 overlay->text_eos = TRUE;
2216 GST_INFO_OBJECT (overlay, "text EOS");
2217 /* wake up the video chain, it might be waiting for a text buffer or
2218 * a text segment update */
2219 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2220 GST_OBJECT_UNLOCK (overlay);
2221 gst_event_unref (event);
2225 ret = gst_pad_event_default (pad, event);
2229 gst_object_unref (overlay);
2235 gst_base_text_overlay_video_event (GstPad * pad, GstEvent * event)
2237 gboolean ret = FALSE;
2238 GstBaseTextOverlay *overlay = NULL;
2240 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2242 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2244 switch (GST_EVENT_TYPE (event)) {
2245 case GST_EVENT_CAPS:
2249 gst_event_parse_caps (event, &caps);
2250 ret = gst_base_text_overlay_setcaps (pad, caps);
2251 gst_event_unref (event);
2254 case GST_EVENT_SEGMENT:
2256 const GstSegment *segment;
2258 GST_DEBUG_OBJECT (overlay, "received new segment");
2260 gst_event_parse_segment (event, &segment);
2262 if (segment->format == GST_FORMAT_TIME) {
2263 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2266 gst_segment_copy_into (segment, &overlay->segment);
2268 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2269 ("received non-TIME newsegment event on video input"));
2272 ret = gst_pad_event_default (pad, event);
2276 GST_OBJECT_LOCK (overlay);
2277 GST_INFO_OBJECT (overlay, "video EOS");
2278 overlay->video_eos = TRUE;
2279 GST_OBJECT_UNLOCK (overlay);
2280 ret = gst_pad_event_default (pad, event);
2282 case GST_EVENT_FLUSH_START:
2283 GST_OBJECT_LOCK (overlay);
2284 GST_INFO_OBJECT (overlay, "video flush start");
2285 overlay->video_flushing = TRUE;
2286 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2287 GST_OBJECT_UNLOCK (overlay);
2288 ret = gst_pad_event_default (pad, event);
2290 case GST_EVENT_FLUSH_STOP:
2291 GST_OBJECT_LOCK (overlay);
2292 GST_INFO_OBJECT (overlay, "video flush stop");
2293 overlay->video_flushing = FALSE;
2294 overlay->video_eos = FALSE;
2295 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2296 GST_OBJECT_UNLOCK (overlay);
2297 ret = gst_pad_event_default (pad, event);
2300 ret = gst_pad_event_default (pad, event);
2304 gst_object_unref (overlay);
2309 /* Called with lock held */
2311 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2313 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2315 if (overlay->text_buffer) {
2316 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2317 overlay->text_buffer);
2318 gst_buffer_unref (overlay->text_buffer);
2319 overlay->text_buffer = NULL;
2322 /* Let the text task know we used that buffer */
2323 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2326 /* We receive text buffers here. If they are out of segment we just ignore them.
2327 If the buffer is in our segment we keep it internally except if another one
2328 is already waiting here, in that case we wait that it gets kicked out */
2329 static GstFlowReturn
2330 gst_base_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
2332 GstFlowReturn ret = GST_FLOW_OK;
2333 GstBaseTextOverlay *overlay = NULL;
2334 gboolean in_seg = FALSE;
2335 guint64 clip_start = 0, clip_stop = 0;
2337 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2339 GST_OBJECT_LOCK (overlay);
2341 if (overlay->text_flushing) {
2342 GST_OBJECT_UNLOCK (overlay);
2343 ret = GST_FLOW_WRONG_STATE;
2344 GST_LOG_OBJECT (overlay, "text flushing");
2348 if (overlay->text_eos) {
2349 GST_OBJECT_UNLOCK (overlay);
2350 ret = GST_FLOW_UNEXPECTED;
2351 GST_LOG_OBJECT (overlay, "text EOS");
2355 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2356 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2357 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2358 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2359 GST_BUFFER_DURATION (buffer)));
2361 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2364 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2365 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2367 stop = GST_CLOCK_TIME_NONE;
2369 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2370 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2376 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2377 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2378 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2379 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2381 /* Wait for the previous buffer to go away */
2382 while (overlay->text_buffer != NULL) {
2383 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2384 GST_DEBUG_PAD_NAME (pad));
2385 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2386 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2387 if (overlay->text_flushing) {
2388 GST_OBJECT_UNLOCK (overlay);
2389 ret = GST_FLOW_WRONG_STATE;
2394 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2395 overlay->text_segment.position = clip_start;
2397 overlay->text_buffer = buffer;
2398 /* That's a new text buffer we need to render */
2399 overlay->need_render = TRUE;
2401 /* in case the video chain is waiting for a text buffer, wake it up */
2402 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2405 GST_OBJECT_UNLOCK (overlay);
2412 static GstFlowReturn
2413 gst_base_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
2415 GstBaseTextOverlayClass *klass;
2416 GstBaseTextOverlay *overlay;
2417 GstFlowReturn ret = GST_FLOW_OK;
2418 gboolean in_seg = FALSE;
2419 guint64 start, stop, clip_start = 0, clip_stop = 0;
2422 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2423 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2425 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2426 goto missing_timestamp;
2428 /* ignore buffers that are outside of the current segment */
2429 start = GST_BUFFER_TIMESTAMP (buffer);
2431 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2432 stop = GST_CLOCK_TIME_NONE;
2434 stop = start + GST_BUFFER_DURATION (buffer);
2437 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2438 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2439 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2441 /* segment_clip() will adjust start unconditionally to segment_start if
2442 * no stop time is provided, so handle this ourselves */
2443 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2444 goto out_of_segment;
2446 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2447 &clip_start, &clip_stop);
2450 goto out_of_segment;
2452 /* if the buffer is only partially in the segment, fix up stamps */
2453 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2454 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2455 buffer = gst_buffer_make_writable (buffer);
2456 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2458 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2461 /* now, after we've done the clipping, fix up end time if there's no
2462 * duration (we only use those estimated values internally though, we
2463 * don't want to set bogus values on the buffer itself) */
2467 gint fps_num, fps_denom;
2469 /* FIXME, store this in setcaps */
2470 caps = gst_pad_get_current_caps (pad);
2471 s = gst_caps_get_structure (caps, 0);
2472 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2473 fps_num && fps_denom) {
2474 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2475 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2477 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2478 stop = start + 1; /* we need to assume some interval */
2480 gst_caps_unref (caps);
2483 gst_object_sync_values (G_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2487 GST_OBJECT_LOCK (overlay);
2489 if (overlay->video_flushing)
2492 if (overlay->video_eos)
2495 if (overlay->silent) {
2496 GST_OBJECT_UNLOCK (overlay);
2497 ret = gst_pad_push (overlay->srcpad, buffer);
2499 /* Update position */
2500 overlay->segment.position = clip_start;
2505 /* Text pad not linked, rendering internal text */
2506 if (!overlay->text_linked) {
2507 if (klass->get_text) {
2508 text = klass->get_text (overlay, buffer);
2510 text = g_strdup (overlay->default_text);
2513 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2514 "text: '%s'", GST_STR_NULL (text));
2516 GST_OBJECT_UNLOCK (overlay);
2518 if (text != NULL && *text != '\0') {
2519 /* Render and push */
2520 gst_base_text_overlay_render_text (overlay, text, -1);
2521 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2523 /* Invalid or empty string */
2524 ret = gst_pad_push (overlay->srcpad, buffer);
2527 /* Text pad linked, check if we have a text buffer queued */
2528 if (overlay->text_buffer) {
2529 gboolean pop_text = FALSE, valid_text_time = TRUE;
2530 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2531 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2532 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2533 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2534 GstClockTime vid_running_time, vid_running_time_end;
2536 /* if the text buffer isn't stamped right, pop it off the
2537 * queue and display it for the current video frame only */
2538 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2539 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2540 GST_WARNING_OBJECT (overlay,
2541 "Got text buffer with invalid timestamp or duration");
2543 valid_text_time = FALSE;
2545 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2546 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2550 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2552 vid_running_time_end =
2553 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2556 /* If timestamp and duration are valid */
2557 if (valid_text_time) {
2559 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2561 text_running_time_end =
2562 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2566 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2567 GST_TIME_ARGS (text_running_time),
2568 GST_TIME_ARGS (text_running_time_end));
2569 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2570 GST_TIME_ARGS (vid_running_time),
2571 GST_TIME_ARGS (vid_running_time_end));
2573 /* Text too old or in the future */
2574 if (valid_text_time && text_running_time_end <= vid_running_time) {
2575 /* text buffer too old, get rid of it and do nothing */
2576 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2578 gst_base_text_overlay_pop_text (overlay);
2579 GST_OBJECT_UNLOCK (overlay);
2580 goto wait_for_text_buf;
2581 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2582 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2583 GST_OBJECT_UNLOCK (overlay);
2584 /* Push the video frame */
2585 ret = gst_pad_push (overlay->srcpad, buffer);
2587 gchar *in_text, *otext;
2588 gsize in_size, osize;
2591 gst_buffer_map (overlay->text_buffer, &osize, NULL, GST_MAP_READ);
2595 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2596 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2597 * here on purpose, this is something that needs fixing upstream */
2598 if (!g_utf8_validate (in_text, in_size, NULL)) {
2599 const gchar *end = NULL;
2601 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2602 in_text = g_strndup (in_text, in_size);
2603 while (!g_utf8_validate (in_text, in_size, &end) && end)
2604 *((gchar *) end) = '*';
2607 /* Get the string */
2608 if (overlay->have_pango_markup) {
2609 text = g_strndup (in_text, in_size);
2611 text = g_markup_escape_text (in_text, in_size);
2614 if (text != NULL && *text != '\0') {
2615 gint text_len = strlen (text);
2617 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2618 text[text_len - 1] == '\r')) {
2621 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2622 gst_base_text_overlay_render_text (overlay, text, text_len);
2624 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2625 gst_base_text_overlay_render_text (overlay, " ", 1);
2627 gst_buffer_unmap (overlay->text_buffer, otext, osize);
2629 if (in_text != otext)
2632 GST_OBJECT_UNLOCK (overlay);
2633 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2635 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2636 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2641 GST_OBJECT_LOCK (overlay);
2642 gst_base_text_overlay_pop_text (overlay);
2643 GST_OBJECT_UNLOCK (overlay);
2646 gboolean wait_for_text_buf = TRUE;
2648 if (overlay->text_eos)
2649 wait_for_text_buf = FALSE;
2651 if (!overlay->wait_text)
2652 wait_for_text_buf = FALSE;
2654 /* Text pad linked, but no text buffer available - what now? */
2655 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2656 GstClockTime text_start_running_time, text_position_running_time;
2657 GstClockTime vid_running_time;
2660 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2661 GST_BUFFER_TIMESTAMP (buffer));
2662 text_start_running_time =
2663 gst_segment_to_running_time (&overlay->text_segment,
2664 GST_FORMAT_TIME, overlay->text_segment.start);
2665 text_position_running_time =
2666 gst_segment_to_running_time (&overlay->text_segment,
2667 GST_FORMAT_TIME, overlay->text_segment.position);
2669 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2670 vid_running_time < text_start_running_time) ||
2671 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2672 vid_running_time < text_position_running_time)) {
2673 wait_for_text_buf = FALSE;
2677 if (wait_for_text_buf) {
2678 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2679 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2680 GST_DEBUG_OBJECT (overlay, "resuming");
2681 GST_OBJECT_UNLOCK (overlay);
2682 goto wait_for_text_buf;
2684 GST_OBJECT_UNLOCK (overlay);
2685 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2686 ret = gst_pad_push (overlay->srcpad, buffer);
2693 /* Update position */
2694 overlay->segment.position = clip_start;
2700 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2701 gst_buffer_unref (buffer);
2707 GST_OBJECT_UNLOCK (overlay);
2708 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2709 gst_buffer_unref (buffer);
2710 return GST_FLOW_WRONG_STATE;
2714 GST_OBJECT_UNLOCK (overlay);
2715 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2716 gst_buffer_unref (buffer);
2717 return GST_FLOW_UNEXPECTED;
2721 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2722 gst_buffer_unref (buffer);
2727 static GstStateChangeReturn
2728 gst_base_text_overlay_change_state (GstElement * element,
2729 GstStateChange transition)
2731 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2732 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2734 switch (transition) {
2735 case GST_STATE_CHANGE_PAUSED_TO_READY:
2736 GST_OBJECT_LOCK (overlay);
2737 overlay->text_flushing = TRUE;
2738 overlay->video_flushing = TRUE;
2739 /* pop_text will broadcast on the GCond and thus also make the video
2740 * chain exit if it's waiting for a text buffer */
2741 gst_base_text_overlay_pop_text (overlay);
2742 GST_OBJECT_UNLOCK (overlay);
2748 ret = parent_class->change_state (element, transition);
2749 if (ret == GST_STATE_CHANGE_FAILURE)
2752 switch (transition) {
2753 case GST_STATE_CHANGE_READY_TO_PAUSED:
2754 GST_OBJECT_LOCK (overlay);
2755 overlay->text_flushing = FALSE;
2756 overlay->video_flushing = FALSE;
2757 overlay->video_eos = FALSE;
2758 overlay->text_eos = FALSE;
2759 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2760 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2761 GST_OBJECT_UNLOCK (overlay);
2771 plugin_init (GstPlugin * plugin)
2773 gst_controller_init (NULL, NULL);
2775 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2776 GST_TYPE_TEXT_OVERLAY) ||
2777 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2778 GST_TYPE_TIME_OVERLAY) ||
2779 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2780 GST_TYPE_CLOCK_OVERLAY) ||
2781 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2782 GST_TYPE_TEXT_RENDER)) {
2786 /*texttestsrc_plugin_init(module, plugin); */
2788 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2793 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2794 "pango", "Pango-based text rendering and overlay", plugin_init,
2795 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)