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 (GstBaseTextOverlay * overlay,
313 static gboolean gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay,
315 static gboolean gst_base_text_overlay_src_event (GstPad * pad,
317 static gboolean gst_base_text_overlay_src_query (GstPad * pad,
320 static gboolean gst_base_text_overlay_video_event (GstPad * pad,
322 static GstFlowReturn gst_base_text_overlay_video_chain (GstPad * pad,
325 static gboolean gst_base_text_overlay_text_event (GstPad * pad,
327 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
329 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
331 static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
332 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
333 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
336 static void gst_base_text_overlay_finalize (GObject * object);
337 static void gst_base_text_overlay_set_property (GObject * object, guint prop_id,
338 const GValue * value, GParamSpec * pspec);
339 static void gst_base_text_overlay_get_property (GObject * object, guint prop_id,
340 GValue * value, GParamSpec * pspec);
342 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
343 PangoFontDescription * desc);
346 gst_base_text_overlay_get_type (void)
348 static GType type = 0;
350 if (g_once_init_enter ((gsize *) & type)) {
351 static const GTypeInfo info = {
352 sizeof (GstBaseTextOverlayClass),
353 (GBaseInitFunc) gst_base_text_overlay_base_init,
355 (GClassInitFunc) gst_base_text_overlay_class_init,
358 sizeof (GstBaseTextOverlay),
360 (GInstanceInitFunc) gst_base_text_overlay_init,
363 g_once_init_leave ((gsize *) & type,
364 g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTextOverlay", &info,
372 gst_base_text_overlay_get_text (GstBaseTextOverlay * overlay,
373 GstBuffer * video_frame)
375 return g_strdup (overlay->default_text);
379 gst_base_text_overlay_base_init (gpointer g_class)
381 GstBaseTextOverlayClass *klass = GST_BASE_TEXT_OVERLAY_CLASS (g_class);
382 PangoFontMap *fontmap;
384 /* Only lock for the subclasses here, the base class
385 * doesn't have this mutex yet and it's not necessary
387 if (klass->pango_lock)
388 g_mutex_lock (klass->pango_lock);
389 fontmap = pango_cairo_font_map_get_default ();
390 klass->pango_context =
391 pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
392 if (klass->pango_lock)
393 g_mutex_unlock (klass->pango_lock);
397 gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
399 GObjectClass *gobject_class;
400 GstElementClass *gstelement_class;
402 gobject_class = (GObjectClass *) klass;
403 gstelement_class = (GstElementClass *) klass;
405 parent_class = g_type_class_peek_parent (klass);
407 gobject_class->finalize = gst_base_text_overlay_finalize;
408 gobject_class->set_property = gst_base_text_overlay_set_property;
409 gobject_class->get_property = gst_base_text_overlay_get_property;
411 gst_element_class_add_pad_template (gstelement_class,
412 gst_static_pad_template_get (&src_template_factory));
413 gst_element_class_add_pad_template (gstelement_class,
414 gst_static_pad_template_get (&video_sink_template_factory));
416 gstelement_class->change_state =
417 GST_DEBUG_FUNCPTR (gst_base_text_overlay_change_state);
419 klass->pango_lock = g_mutex_new ();
421 klass->get_text = gst_base_text_overlay_get_text;
423 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
424 g_param_spec_string ("text", "text",
425 "Text to be display.", DEFAULT_PROP_TEXT,
426 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
427 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
428 g_param_spec_boolean ("shaded-background", "shaded background",
429 "Whether to shade the background under the text area",
430 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
431 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
432 g_param_spec_enum ("valignment", "vertical alignment",
433 "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
434 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
435 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
436 g_param_spec_enum ("halignment", "horizontal alignment",
437 "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
438 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
439 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
440 g_param_spec_string ("valign", "vertical alignment",
441 "Vertical alignment of the text (deprecated; use valignment)",
442 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
443 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
444 g_param_spec_string ("halign", "horizontal alignment",
445 "Horizontal alignment of the text (deprecated; use halignment)",
446 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
447 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
448 g_param_spec_int ("xpad", "horizontal paddding",
449 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
450 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
451 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
452 g_param_spec_int ("ypad", "vertical padding",
453 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
454 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
455 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
456 g_param_spec_int ("deltax", "X position modifier",
457 "Shift X position to the left or to the right. Unit is pixels.",
458 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
459 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
460 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
461 g_param_spec_int ("deltay", "Y position modifier",
462 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
463 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
465 * GstBaseTextOverlay:xpos
467 * Horizontal position of the rendered text when using positioned alignment.
471 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
472 g_param_spec_double ("xpos", "horizontal position",
473 "Horizontal position when using position alignment", 0, 1.0,
475 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
477 * GstBaseTextOverlay:ypos
479 * Vertical position of the rendered text when using positioned alignment.
483 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
484 g_param_spec_double ("ypos", "vertical position",
485 "Vertical position when using position alignment", 0, 1.0,
487 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
488 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
489 g_param_spec_enum ("wrap-mode", "wrap mode",
490 "Whether to wrap the text and if so how.",
491 GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
492 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
493 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
494 g_param_spec_string ("font-desc", "font description",
495 "Pango font description of font to be used for rendering. "
496 "See documentation of pango_font_description_from_string "
497 "for syntax.", DEFAULT_PROP_FONT_DESC,
498 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
500 * GstBaseTextOverlay:color
502 * Color of the rendered text.
506 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
507 g_param_spec_uint ("color", "Color",
508 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
510 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
512 * GstTextOverlay:outline-color
514 * Color of the outline of the rendered text.
518 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
519 g_param_spec_uint ("outline-color", "Text Outline Color",
520 "Color to use for outline the text (big-endian ARGB).", 0,
521 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
522 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
525 * GstBaseTextOverlay:line-alignment
527 * Alignment of text lines relative to each other (for multi-line text)
531 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
532 g_param_spec_enum ("line-alignment", "line alignment",
533 "Alignment of text lines relative to each other.",
534 GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
535 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
537 * GstBaseTextOverlay:silent
539 * If set, no text is rendered. Useful to switch off text rendering
540 * temporarily without removing the textoverlay element from the pipeline.
544 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
545 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
546 g_param_spec_boolean ("silent", "silent",
547 "Whether to render the text string",
549 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
551 * GstBaseTextOverlay:wait-text
553 * If set, the video will block until a subtitle is received on the text pad.
554 * If video and subtitles are sent in sync, like from the same demuxer, this
555 * property should be set.
559 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
560 g_param_spec_boolean ("wait-text", "Wait Text",
561 "Whether to wait for subtitles",
562 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
564 g_object_class_install_property (G_OBJECT_CLASS (klass),
565 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
566 "Automatically adjust font size to screen-size.",
567 DEFAULT_PROP_AUTO_ADJUST_SIZE,
568 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
570 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
571 g_param_spec_boolean ("vertical-render", "vertical render",
572 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
573 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
577 gst_base_text_overlay_finalize (GObject * object)
579 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
581 g_free (overlay->default_text);
583 if (overlay->text_image) {
584 g_free (overlay->text_image);
585 overlay->text_image = NULL;
588 if (overlay->layout) {
589 g_object_unref (overlay->layout);
590 overlay->layout = NULL;
593 if (overlay->text_buffer) {
594 gst_buffer_unref (overlay->text_buffer);
595 overlay->text_buffer = NULL;
599 g_cond_free (overlay->cond);
600 overlay->cond = NULL;
603 G_OBJECT_CLASS (parent_class)->finalize (object);
607 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
608 GstBaseTextOverlayClass * klass)
610 GstPadTemplate *template;
611 PangoFontDescription *desc;
614 template = gst_static_pad_template_get (&video_sink_template_factory);
615 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
616 gst_object_unref (template);
617 gst_pad_set_getcaps_function (overlay->video_sinkpad,
618 GST_DEBUG_FUNCPTR (gst_base_text_overlay_getcaps));
619 gst_pad_set_event_function (overlay->video_sinkpad,
620 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_event));
621 gst_pad_set_chain_function (overlay->video_sinkpad,
622 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
623 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
626 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
630 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
631 gst_object_unref (template);
633 gst_pad_set_event_function (overlay->text_sinkpad,
634 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
635 gst_pad_set_chain_function (overlay->text_sinkpad,
636 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
637 gst_pad_set_link_function (overlay->text_sinkpad,
638 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
639 gst_pad_set_unlink_function (overlay->text_sinkpad,
640 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
641 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
645 template = gst_static_pad_template_get (&src_template_factory);
646 overlay->srcpad = gst_pad_new_from_template (template, "src");
647 gst_object_unref (template);
648 gst_pad_set_getcaps_function (overlay->srcpad,
649 GST_DEBUG_FUNCPTR (gst_base_text_overlay_getcaps));
650 gst_pad_set_event_function (overlay->srcpad,
651 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_event));
652 gst_pad_set_query_function (overlay->srcpad,
653 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_query));
654 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
656 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
657 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
659 pango_layout_new (GST_BASE_TEXT_OVERLAY_GET_CLASS
660 (overlay)->pango_context);
662 pango_context_get_font_description (GST_BASE_TEXT_OVERLAY_GET_CLASS
663 (overlay)->pango_context);
664 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
666 overlay->color = DEFAULT_PROP_COLOR;
667 overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
668 overlay->halign = DEFAULT_PROP_HALIGNMENT;
669 overlay->valign = DEFAULT_PROP_VALIGNMENT;
670 overlay->xpad = DEFAULT_PROP_XPAD;
671 overlay->ypad = DEFAULT_PROP_YPAD;
672 overlay->deltax = DEFAULT_PROP_DELTAX;
673 overlay->deltay = DEFAULT_PROP_DELTAY;
674 overlay->xpos = DEFAULT_PROP_XPOS;
675 overlay->ypos = DEFAULT_PROP_YPOS;
677 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
679 overlay->want_shading = DEFAULT_PROP_SHADING;
680 overlay->shading_value = DEFAULT_SHADING_VALUE;
681 overlay->silent = DEFAULT_PROP_SILENT;
682 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
683 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
685 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
686 overlay->need_render = TRUE;
687 overlay->text_image = NULL;
688 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
689 gst_base_text_overlay_update_render_mode (overlay);
691 overlay->text_buffer = NULL;
692 overlay->text_linked = FALSE;
693 overlay->cond = g_cond_new ();
694 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
695 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
699 gst_base_text_overlay_update_wrap_mode (GstBaseTextOverlay * overlay)
701 if (overlay->wrap_mode == GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE) {
702 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
703 pango_layout_set_width (overlay->layout, -1);
707 if (overlay->auto_adjust_size) {
708 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
709 if (overlay->use_vertical_render) {
710 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
714 (overlay->use_vertical_render ? overlay->height : overlay->width) *
718 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
719 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
720 pango_layout_set_width (overlay->layout, width);
721 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
726 gst_base_text_overlay_update_render_mode (GstBaseTextOverlay * overlay)
728 PangoMatrix matrix = PANGO_MATRIX_INIT;
729 PangoContext *context = pango_layout_get_context (overlay->layout);
731 if (overlay->use_vertical_render) {
732 pango_matrix_rotate (&matrix, -90);
733 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
734 pango_context_set_matrix (context, &matrix);
735 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
737 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
738 pango_context_set_matrix (context, &matrix);
739 pango_layout_set_alignment (overlay->layout, overlay->line_align);
744 gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay, GstCaps * caps)
746 GstStructure *structure;
748 structure = gst_caps_get_structure (caps, 0);
749 overlay->have_pango_markup =
750 gst_structure_has_name (structure, "text/x-pango-markup");
755 /* FIXME: upstream nego (e.g. when the video window is resized) */
758 gst_base_text_overlay_setcaps (GstBaseTextOverlay * overlay, GstCaps * caps)
761 gboolean ret = FALSE;
763 if (!gst_video_info_from_caps (&info, caps))
766 overlay->info = info;
767 overlay->format = GST_VIDEO_INFO_FORMAT (&info);
768 overlay->width = GST_VIDEO_INFO_WIDTH (&info);
769 overlay->height = GST_VIDEO_INFO_HEIGHT (&info);
771 ret = gst_pad_push_event (overlay->srcpad, gst_event_new_caps (caps));
774 GST_OBJECT_LOCK (overlay);
775 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
776 gst_base_text_overlay_update_wrap_mode (overlay);
777 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
778 GST_OBJECT_UNLOCK (overlay);
786 GST_DEBUG_OBJECT (overlay, "could not parse caps");
792 gst_base_text_overlay_set_property (GObject * object, guint prop_id,
793 const GValue * value, GParamSpec * pspec)
795 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
797 GST_OBJECT_LOCK (overlay);
800 g_free (overlay->default_text);
801 overlay->default_text = g_value_dup_string (value);
802 overlay->need_render = TRUE;
805 overlay->want_shading = g_value_get_boolean (value);
808 overlay->xpad = g_value_get_int (value);
811 overlay->ypad = g_value_get_int (value);
814 overlay->deltax = g_value_get_int (value);
817 overlay->deltay = g_value_get_int (value);
820 overlay->xpos = g_value_get_double (value);
823 overlay->ypos = g_value_get_double (value);
826 const gchar *s = g_value_get_string (value);
828 if (s && g_ascii_strcasecmp (s, "left") == 0)
829 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_LEFT;
830 else if (s && g_ascii_strcasecmp (s, "center") == 0)
831 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_CENTER;
832 else if (s && g_ascii_strcasecmp (s, "right") == 0)
833 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
835 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
840 const gchar *s = g_value_get_string (value);
842 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
843 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE;
844 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
845 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM;
846 else if (s && g_ascii_strcasecmp (s, "top") == 0)
847 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
849 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
853 case PROP_VALIGNMENT:
854 overlay->valign = g_value_get_enum (value);
856 case PROP_HALIGNMENT:
857 overlay->halign = g_value_get_enum (value);
860 overlay->wrap_mode = g_value_get_enum (value);
861 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
862 gst_base_text_overlay_update_wrap_mode (overlay);
863 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
867 PangoFontDescription *desc;
868 const gchar *fontdesc_str;
870 fontdesc_str = g_value_get_string (value);
871 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
872 desc = pango_font_description_from_string (fontdesc_str);
874 GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
875 pango_layout_set_font_description (overlay->layout, desc);
876 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
877 pango_font_description_free (desc);
879 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
882 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
886 overlay->color = g_value_get_uint (value);
888 case PROP_OUTLINE_COLOR:
889 overlay->outline_color = g_value_get_uint (value);
892 overlay->silent = g_value_get_boolean (value);
894 case PROP_LINE_ALIGNMENT:
895 overlay->line_align = g_value_get_enum (value);
896 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
897 pango_layout_set_alignment (overlay->layout,
898 (PangoAlignment) overlay->line_align);
899 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
902 overlay->wait_text = g_value_get_boolean (value);
904 case PROP_AUTO_ADJUST_SIZE:
905 overlay->auto_adjust_size = g_value_get_boolean (value);
906 overlay->need_render = TRUE;
908 case PROP_VERTICAL_RENDER:
909 overlay->use_vertical_render = g_value_get_boolean (value);
910 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
911 gst_base_text_overlay_update_render_mode (overlay);
912 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
913 overlay->need_render = TRUE;
916 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
920 overlay->need_render = TRUE;
921 GST_OBJECT_UNLOCK (overlay);
925 gst_base_text_overlay_get_property (GObject * object, guint prop_id,
926 GValue * value, GParamSpec * pspec)
928 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
930 GST_OBJECT_LOCK (overlay);
933 g_value_set_string (value, overlay->default_text);
936 g_value_set_boolean (value, overlay->want_shading);
939 g_value_set_int (value, overlay->xpad);
942 g_value_set_int (value, overlay->ypad);
945 g_value_set_int (value, overlay->deltax);
948 g_value_set_int (value, overlay->deltay);
951 g_value_set_double (value, overlay->xpos);
954 g_value_set_double (value, overlay->ypos);
956 case PROP_VALIGNMENT:
957 g_value_set_enum (value, overlay->valign);
959 case PROP_HALIGNMENT:
960 g_value_set_enum (value, overlay->halign);
963 g_value_set_enum (value, overlay->wrap_mode);
966 g_value_set_boolean (value, overlay->silent);
968 case PROP_LINE_ALIGNMENT:
969 g_value_set_enum (value, overlay->line_align);
972 g_value_set_boolean (value, overlay->wait_text);
974 case PROP_AUTO_ADJUST_SIZE:
975 g_value_set_boolean (value, overlay->auto_adjust_size);
977 case PROP_VERTICAL_RENDER:
978 g_value_set_boolean (value, overlay->use_vertical_render);
981 g_value_set_uint (value, overlay->color);
983 case PROP_OUTLINE_COLOR:
984 g_value_set_uint (value, overlay->outline_color);
987 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
991 overlay->need_render = TRUE;
992 GST_OBJECT_UNLOCK (overlay);
996 gst_base_text_overlay_src_query (GstPad * pad, GstQuery * query)
998 gboolean ret = FALSE;
999 GstBaseTextOverlay *overlay = NULL;
1001 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1002 if (G_UNLIKELY (!overlay))
1005 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1007 gst_object_unref (overlay);
1013 gst_base_text_overlay_src_event (GstPad * pad, GstEvent * event)
1015 gboolean ret = FALSE;
1016 GstBaseTextOverlay *overlay = NULL;
1018 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1019 if (G_UNLIKELY (!overlay)) {
1020 gst_event_unref (event);
1024 switch (GST_EVENT_TYPE (event)) {
1025 case GST_EVENT_SEEK:{
1028 /* We don't handle seek if we have not text pad */
1029 if (!overlay->text_linked) {
1030 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1031 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1035 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1037 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1039 /* Flush downstream, only for flushing seek */
1040 if (flags & GST_SEEK_FLAG_FLUSH)
1041 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1043 /* Mark ourself as flushing, unblock chains */
1044 GST_OBJECT_LOCK (overlay);
1045 overlay->video_flushing = TRUE;
1046 overlay->text_flushing = TRUE;
1047 gst_base_text_overlay_pop_text (overlay);
1048 GST_OBJECT_UNLOCK (overlay);
1050 /* Seek on each sink pad */
1051 gst_event_ref (event);
1052 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1054 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1056 gst_event_unref (event);
1061 if (overlay->text_linked) {
1062 gst_event_ref (event);
1063 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1064 gst_pad_push_event (overlay->text_sinkpad, event);
1066 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1072 gst_object_unref (overlay);
1078 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1080 GstBaseTextOverlay *overlay;
1084 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1085 if (G_UNLIKELY (!overlay))
1086 return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1088 if (pad == overlay->srcpad)
1089 otherpad = overlay->video_sinkpad;
1091 otherpad = overlay->srcpad;
1093 /* we can do what the peer can */
1094 caps = gst_pad_peer_get_caps (otherpad, filter);
1096 GstCaps *temp, *templ;
1098 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1100 /* filtered against our padtemplate */
1101 templ = gst_pad_get_pad_template_caps (otherpad);
1102 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1103 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1104 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1105 gst_caps_unref (caps);
1106 gst_caps_unref (templ);
1107 /* this is what we can do */
1110 /* no peer, our padtemplate is enough then */
1111 caps = gst_pad_get_pad_template_caps (pad);
1113 GstCaps *intersection;
1116 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1117 gst_caps_unref (caps);
1118 caps = intersection;
1122 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1124 gst_object_unref (overlay);
1130 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1131 PangoFontDescription * desc)
1133 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1134 overlay->shadow_offset = (double) (font_size) / 13.0;
1135 overlay->outline_offset = (double) (font_size) / 15.0;
1136 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1137 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1140 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1141 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1142 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1143 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1147 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1148 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1155 gint width = overlay->image_width;
1156 gint height = overlay->image_height;
1162 if (xpos + width > overlay->width) {
1163 width = overlay->width - xpos;
1166 if (ypos + height > overlay->height) {
1167 height = overlay->height - ypos;
1170 dest += (ypos / 1) * dest_stride;
1172 for (i = 0; i < height; i++) {
1173 pimage = text_image + 4 * (i * overlay->image_width);
1174 py = dest + i * dest_stride + xpos;
1175 for (j = 0; j < width; j++) {
1176 b = pimage[CAIRO_ARGB_B];
1177 g = pimage[CAIRO_ARGB_G];
1178 r = pimage[CAIRO_ARGB_R];
1179 a = pimage[CAIRO_ARGB_A];
1180 CAIRO_UNPREMULTIPLY (a, r, g, b);
1187 COMP_Y (y, r, g, b);
1189 BLEND (*py++, a, y, x);
1195 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1196 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1197 guint destcb_stride, guint destcr_stride, guint pix_stride)
1202 gushort r1, g1, b1, a1;
1203 guchar *pimage1, *pimage2;
1205 gint width = overlay->image_width - 2;
1206 gint height = overlay->image_height - 2;
1214 if (xpos + width > overlay->width) {
1215 width = overlay->width - xpos;
1218 if (ypos + height > overlay->height) {
1219 height = overlay->height - ypos;
1222 destcb += (ypos / 2) * destcb_stride;
1223 destcr += (ypos / 2) * destcr_stride;
1225 for (i = 0; i < height; i += 2) {
1226 pimage1 = text_image + 4 * (i * overlay->image_width);
1227 pimage2 = pimage1 + 4 * overlay->image_width;
1228 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1229 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1230 for (j = 0; j < width; j += 2) {
1231 b = pimage1[CAIRO_ARGB_B];
1232 g = pimage1[CAIRO_ARGB_G];
1233 r = pimage1[CAIRO_ARGB_R];
1234 a = pimage1[CAIRO_ARGB_A];
1235 CAIRO_UNPREMULTIPLY (a, r, g, b);
1238 b1 = pimage1[CAIRO_ARGB_B];
1239 g1 = pimage1[CAIRO_ARGB_G];
1240 r1 = pimage1[CAIRO_ARGB_R];
1241 a1 = pimage1[CAIRO_ARGB_A];
1242 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1249 b1 = pimage2[CAIRO_ARGB_B];
1250 g1 = pimage2[CAIRO_ARGB_G];
1251 r1 = pimage2[CAIRO_ARGB_R];
1252 a1 = pimage2[CAIRO_ARGB_A];
1253 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1260 /* + 2 for rounding */
1261 b1 = pimage2[CAIRO_ARGB_B];
1262 g1 = pimage2[CAIRO_ARGB_G];
1263 r1 = pimage2[CAIRO_ARGB_R];
1264 a1 = pimage2[CAIRO_ARGB_A];
1265 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1282 COMP_U (cb, r, g, b);
1283 COMP_V (cr, r, g, b);
1286 BLEND (*pcb, a, cb, x);
1288 BLEND (*pcr, a, cr, x);
1297 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1298 const gchar * string, gint textlen)
1301 cairo_surface_t *surface;
1302 PangoRectangle ink_rect, logical_rect;
1303 cairo_matrix_t cairo_matrix;
1305 double scalef = 1.0;
1308 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1310 if (overlay->auto_adjust_size) {
1311 /* 640 pixel is default */
1312 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1314 pango_layout_set_width (overlay->layout, -1);
1315 /* set text on pango layout */
1316 pango_layout_set_markup (overlay->layout, string, textlen);
1318 /* get subtitle image size */
1319 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1321 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1323 if (width + overlay->deltax >
1324 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1326 * subtitle image width is larger then overlay width
1327 * so rearrange overlay wrap mode.
1329 gst_base_text_overlay_update_wrap_mode (overlay);
1330 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1331 width = overlay->width;
1335 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1336 if (height > overlay->height) {
1337 height = overlay->height;
1339 if (overlay->use_vertical_render) {
1340 PangoRectangle rect;
1341 PangoContext *context;
1342 PangoMatrix matrix = PANGO_MATRIX_INIT;
1345 context = pango_layout_get_context (overlay->layout);
1347 pango_matrix_rotate (&matrix, -90);
1349 rect.x = rect.y = 0;
1351 rect.height = height;
1352 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1353 matrix.x0 = -rect.x;
1354 matrix.y0 = -rect.y;
1356 pango_context_set_matrix (context, &matrix);
1358 cairo_matrix.xx = matrix.xx;
1359 cairo_matrix.yx = matrix.yx;
1360 cairo_matrix.xy = matrix.xy;
1361 cairo_matrix.yy = matrix.yy;
1362 cairo_matrix.x0 = matrix.x0;
1363 cairo_matrix.y0 = matrix.y0;
1364 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1370 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1373 /* reallocate surface */
1374 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1376 surface = cairo_image_surface_create_for_data (overlay->text_image,
1377 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1378 cr = cairo_create (surface);
1381 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1384 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1386 if (overlay->want_shading)
1387 cairo_paint_with_alpha (cr, overlay->shading_value);
1389 /* apply transformations */
1390 cairo_set_matrix (cr, &cairo_matrix);
1392 /* FIXME: We use show_layout everywhere except for the surface
1393 * because it's really faster and internally does all kinds of
1394 * caching. Unfortunately we have to paint to a cairo path for
1395 * the outline and this is slow. Once Pango supports user fonts
1396 * we should use them, see
1397 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1399 * Idea would the be, to create a cairo user font that
1400 * does shadow, outline, text painting in the
1401 * render_glyph function.
1404 /* draw shadow text */
1406 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1407 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1408 pango_cairo_show_layout (cr, overlay->layout);
1411 a = (overlay->outline_color >> 24) & 0xff;
1412 r = (overlay->outline_color >> 16) & 0xff;
1413 g = (overlay->outline_color >> 8) & 0xff;
1414 b = (overlay->outline_color >> 0) & 0xff;
1416 /* draw outline text */
1418 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1419 cairo_set_line_width (cr, overlay->outline_offset);
1420 pango_cairo_layout_path (cr, overlay->layout);
1424 a = (overlay->color >> 24) & 0xff;
1425 r = (overlay->color >> 16) & 0xff;
1426 g = (overlay->color >> 8) & 0xff;
1427 b = (overlay->color >> 0) & 0xff;
1431 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1432 pango_cairo_show_layout (cr, overlay->layout);
1436 cairo_surface_destroy (surface);
1437 overlay->image_width = width;
1438 overlay->image_height = height;
1439 overlay->baseline_y = ink_rect.y;
1440 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1447 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1448 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1450 gint i, j, dest_stride;
1453 dest_stride = dest->info.stride[0];
1454 dest_ptr = dest->data[0];
1456 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1457 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1459 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1460 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1462 for (i = y0; i < y1; ++i) {
1463 for (j = x0; j < x1; ++j) {
1464 gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
1466 dest_ptr[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1472 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1473 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1476 guint dest_stride, pixel_stride;
1479 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
1480 dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
1481 pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
1483 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1484 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1486 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1487 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1490 x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
1492 x1 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x1);
1495 y0 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y0);
1497 y1 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y1);
1499 for (i = y0; i < y1; i++) {
1500 for (j = x0; j < x1; j++) {
1504 y_pos = (i * dest_stride) + j * pixel_stride;
1505 y = dest_ptr[y_pos] + overlay->shading_value;
1507 dest_ptr[y_pos] = CLAMP (y, 0, 255);
1512 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1513 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1514 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1516 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
1517 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1522 dest_ptr = dest->data[0];
1524 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1525 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1527 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1528 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1530 for (i = y0; i < y1; i++) {
1531 for (j = x0; j < x1; j++) {
1534 y_pos = (i * 4 * overlay->width) + j * 4;
1535 for (k = 0; k < 4; k++) {
1536 y = dest_ptr[y_pos + k] + overlay->shading_value;
1537 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);
1543 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1544 static inline void \
1545 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
1546 gint x0, gint x1, gint y0, gint y1) \
1551 dest_ptr = dest->data[0];\
1553 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1554 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1556 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1557 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1559 for (i = y0; i < y1; i++) {\
1560 for (j = x0; j < x1; j++) {\
1562 y_pos = (i * 4 * overlay->width) + j * 4;\
1563 for (k = OFFSET; k < 3+OFFSET; k++) {\
1564 y = dest_ptr[y_pos + k] + overlay->shading_value;\
1565 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);\
1570 ARGB_SHADE_FUNCTION (ARGB, 1);
1571 ARGB_SHADE_FUNCTION (ABGR, 1);
1572 ARGB_SHADE_FUNCTION (RGBA, 0);
1573 ARGB_SHADE_FUNCTION (BGRA, 0);
1577 * - use proper strides and offset for I420
1578 * - don't draw over the edge of the picture (try a longer
1579 * text with a huge font size)
1583 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1584 GstVideoFrame * dest, gint xpos, gint ypos)
1586 int y_stride, u_stride, v_stride;
1587 guint8 *y_pixels, *u_pixels, *v_pixels;
1589 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1590 * to a boundary of integer number of U/V pixels:
1592 xpos = GST_ROUND_UP_2 (xpos);
1593 ypos = GST_ROUND_UP_2 (ypos);
1595 y_pixels = dest->data[0];
1596 u_pixels = dest->data[1];
1597 v_pixels = dest->data[2];
1598 y_stride = dest->info.stride[0];
1599 u_stride = dest->info.stride[1];
1600 v_stride = dest->info.stride[2];
1602 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1603 overlay->text_image, y_stride);
1604 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1605 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 2);
1609 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1610 GstVideoFrame * dest, gint xpos, gint ypos)
1612 int y_stride, u_stride, v_stride;
1613 guint8 *y_pixels, *u_pixels, *v_pixels;
1615 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1616 * to a boundary of integer number of U/V pixels:
1618 xpos = GST_ROUND_UP_2 (xpos);
1619 ypos = GST_ROUND_UP_2 (ypos);
1621 y_pixels = dest->data[0];
1622 u_pixels = dest->data[1];
1623 v_pixels = dest->data[2];
1624 y_stride = dest->info.stride[0];
1625 u_stride = dest->info.stride[1];
1626 v_stride = dest->info.stride[2];
1628 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1629 overlay->text_image, y_stride);
1630 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1631 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 1);
1635 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1636 GstVideoFrame * dest, gint xpos, gint ypos)
1643 guchar *pimage, *dest_ptr;
1646 yuv_pixels = dest->data[0];
1648 /* because U/V is 2x horizontally subsampled, we need to round to a
1649 * boundary of integer number of U/V pixels in x dimension:
1651 xpos = GST_ROUND_UP_2 (xpos);
1653 w = overlay->image_width - 2;
1654 h = overlay->image_height - 2;
1660 if (xpos + w > overlay->width) {
1661 w = overlay->width - xpos;
1664 if (ypos + h > overlay->height) {
1665 h = overlay->height - ypos;
1668 for (i = 0; i < h; i++) {
1669 pimage = overlay->text_image + i * overlay->image_width * 4;
1670 dest_ptr = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1671 for (j = 0; j < w; j += 2) {
1672 b0 = pimage[CAIRO_ARGB_B];
1673 g0 = pimage[CAIRO_ARGB_G];
1674 r0 = pimage[CAIRO_ARGB_R];
1675 a0 = pimage[CAIRO_ARGB_A];
1676 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1679 b1 = pimage[CAIRO_ARGB_B];
1680 g1 = pimage[CAIRO_ARGB_G];
1681 r1 = pimage[CAIRO_ARGB_R];
1682 a1 = pimage[CAIRO_ARGB_A];
1683 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1693 COMP_Y (y0, r0, g0, b0);
1694 COMP_Y (y1, r1, g1, b1);
1704 COMP_U (u, r0, g0, b0);
1705 COMP_V (v, r0, g0, b0);
1707 BLEND (*dest_ptr, a0, u, *dest_ptr);
1709 BLEND (*dest_ptr, a0, y0, *dest_ptr);
1711 BLEND (*dest_ptr, a0, v, *dest_ptr);
1713 BLEND (*dest_ptr, a0, y1, *dest_ptr);
1720 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1721 GstVideoFrame * dest, gint xpos, gint ypos)
1727 guchar *pimage, *dest_ptr;
1730 rgb_pixels = dest->data[0];
1732 w = overlay->image_width;
1733 h = overlay->image_height;
1739 if (xpos + w > overlay->width) {
1740 w = overlay->width - xpos;
1743 if (ypos + h > overlay->height) {
1744 h = overlay->height - ypos;
1747 for (i = 0; i < h; i++) {
1748 pimage = overlay->text_image + i * overlay->image_width * 4;
1749 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1750 for (j = 0; j < w; j++) {
1751 a = pimage[CAIRO_ARGB_A];
1752 b = pimage[CAIRO_ARGB_B];
1753 g = pimage[CAIRO_ARGB_G];
1754 r = pimage[CAIRO_ARGB_R];
1756 CAIRO_UNPREMULTIPLY (a, r, g, b);
1758 // convert background to yuv
1759 COMP_Y (y, r, g, b);
1760 COMP_U (u, r, g, b);
1761 COMP_V (v, r, g, b);
1763 // preform text "OVER" background alpha compositing
1764 a1 = a + (dest_ptr[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1765 OVER (dest_ptr[1], a, y, dest_ptr[0], dest_ptr[1], a1);
1766 OVER (dest_ptr[2], a, u, dest_ptr[0], dest_ptr[2], a1);
1767 OVER (dest_ptr[3], a, v, dest_ptr[0], dest_ptr[3], a1);
1768 dest_ptr[0] = a1 - 1; // remove the temporary 1 we added
1776 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1777 static inline void \
1778 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1779 GstVideoFrame * dest, gint xpos, gint ypos) \
1784 guchar *pimage, *dest_ptr; \
1785 guint8 *rgb_pixels;\
1787 rgb_pixels = dest->data[0];\
1789 w = overlay->image_width; \
1790 h = overlay->image_height; \
1796 if (xpos + w > overlay->width) { \
1797 w = overlay->width - xpos; \
1800 if (ypos + h > overlay->height) { \
1801 h = overlay->height - ypos; \
1804 for (i = 0; i < h; i++) { \
1805 pimage = overlay->text_image + i * overlay->image_width * 4; \
1806 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1807 for (j = 0; j < w; j++) { \
1808 a = pimage[CAIRO_ARGB_A]; \
1809 b = pimage[CAIRO_ARGB_B]; \
1810 g = pimage[CAIRO_ARGB_G]; \
1811 r = pimage[CAIRO_ARGB_R]; \
1812 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1813 b = (b*a + dest_ptr[B] * (255-a)) / 255; \
1814 g = (g*a + dest_ptr[G] * (255-a)) / 255; \
1815 r = (r*a + dest_ptr[R] * (255-a)) / 255; \
1825 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1826 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1827 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1828 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1830 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1831 static inline void \
1832 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1833 GstVideoFrame * dest, gint xpos, gint ypos) \
1835 int a, r, g, b, a1; \
1838 guchar *pimage, *dest_ptr; \
1839 guint8 *rgb_pixels;\
1841 rgb_pixels = dest->data[0];\
1843 w = overlay->image_width; \
1844 h = overlay->image_height; \
1850 if (xpos + w > overlay->width) { \
1851 w = overlay->width - xpos; \
1854 if (ypos + h > overlay->height) { \
1855 h = overlay->height - ypos; \
1858 for (i = 0; i < h; i++) { \
1859 pimage = overlay->text_image + i * overlay->image_width * 4; \
1860 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1861 for (j = 0; j < w; j++) { \
1862 a = pimage[CAIRO_ARGB_A]; \
1863 b = pimage[CAIRO_ARGB_B]; \
1864 g = pimage[CAIRO_ARGB_G]; \
1865 r = pimage[CAIRO_ARGB_R]; \
1866 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1867 a1 = a + (dest_ptr[A] * (255 - a)) / 255 + 1; \
1868 OVER (dest_ptr[R], a, r, dest_ptr[0], dest_ptr[R], a1); \
1869 OVER (dest_ptr[G], a, g, dest_ptr[0], dest_ptr[G], a1); \
1870 OVER (dest_ptr[B], a, b, dest_ptr[0], dest_ptr[B], a1); \
1871 dest_ptr[A] = a1 - 1; \
1877 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1878 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1879 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1880 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1883 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1884 const gchar * text, gint textlen)
1888 if (!overlay->need_render) {
1889 GST_DEBUG ("Using previously rendered text.");
1893 /* -1 is the whole string */
1894 if (text != NULL && textlen < 0) {
1895 textlen = strlen (text);
1899 string = g_strndup (text, textlen);
1900 } else { /* empty string */
1901 string = g_strdup (" ");
1903 g_strdelimit (string, "\r\t", ' ');
1904 textlen = strlen (string);
1906 /* FIXME: should we check for UTF-8 here? */
1908 GST_DEBUG ("Rendering '%s'", string);
1909 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1913 overlay->need_render = FALSE;
1916 static GstFlowReturn
1917 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1918 GstBuffer * video_frame)
1922 GstBaseTextOverlayVAlign valign;
1923 GstBaseTextOverlayHAlign halign;
1924 GstVideoFrame frame;
1926 width = overlay->image_width;
1927 height = overlay->image_height;
1929 video_frame = gst_buffer_make_writable (video_frame);
1931 if (!gst_video_frame_map (&frame, &overlay->info, video_frame, GST_MAP_WRITE))
1934 if (overlay->use_vertical_render)
1935 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1937 halign = overlay->halign;
1940 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1941 xpos = overlay->xpad;
1943 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1944 xpos = (overlay->width - width) / 2;
1946 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1947 xpos = overlay->width - width - overlay->xpad;
1949 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1950 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1951 xpos = CLAMP (xpos, 0, overlay->width - width);
1958 xpos += overlay->deltax;
1960 if (overlay->use_vertical_render)
1961 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1963 valign = overlay->valign;
1966 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1967 ypos = overlay->height - height - overlay->ypad;
1969 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1970 ypos = overlay->height - (height + overlay->ypad);
1972 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1973 ypos = overlay->ypad;
1975 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1976 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1977 ypos = CLAMP (ypos, 0, overlay->height - height);
1979 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1980 ypos = (overlay->height - height) / 2;
1983 ypos = overlay->ypad;
1986 ypos += overlay->deltay;
1988 /* shaded background box */
1989 if (overlay->want_shading) {
1990 switch (overlay->format) {
1991 case GST_VIDEO_FORMAT_I420:
1992 case GST_VIDEO_FORMAT_NV12:
1993 case GST_VIDEO_FORMAT_NV21:
1994 gst_base_text_overlay_shade_planar_Y (overlay, &frame,
1995 xpos, xpos + overlay->image_width,
1996 ypos, ypos + overlay->image_height);
1998 case GST_VIDEO_FORMAT_AYUV:
1999 case GST_VIDEO_FORMAT_UYVY:
2000 gst_base_text_overlay_shade_packed_Y (overlay, &frame,
2001 xpos, xpos + overlay->image_width,
2002 ypos, ypos + overlay->image_height);
2004 case GST_VIDEO_FORMAT_xRGB:
2005 gst_base_text_overlay_shade_xRGB (overlay, &frame,
2006 xpos, xpos + overlay->image_width,
2007 ypos, ypos + overlay->image_height);
2009 case GST_VIDEO_FORMAT_xBGR:
2010 gst_base_text_overlay_shade_xBGR (overlay, &frame,
2011 xpos, xpos + overlay->image_width,
2012 ypos, ypos + overlay->image_height);
2014 case GST_VIDEO_FORMAT_BGRx:
2015 gst_base_text_overlay_shade_BGRx (overlay, &frame,
2016 xpos, xpos + overlay->image_width,
2017 ypos, ypos + overlay->image_height);
2019 case GST_VIDEO_FORMAT_RGBx:
2020 gst_base_text_overlay_shade_RGBx (overlay, &frame,
2021 xpos, xpos + overlay->image_width,
2022 ypos, ypos + overlay->image_height);
2024 case GST_VIDEO_FORMAT_ARGB:
2025 gst_base_text_overlay_shade_ARGB (overlay, &frame,
2026 xpos, xpos + overlay->image_width,
2027 ypos, ypos + overlay->image_height);
2029 case GST_VIDEO_FORMAT_ABGR:
2030 gst_base_text_overlay_shade_ABGR (overlay, &frame,
2031 xpos, xpos + overlay->image_width,
2032 ypos, ypos + overlay->image_height);
2034 case GST_VIDEO_FORMAT_RGBA:
2035 gst_base_text_overlay_shade_RGBA (overlay, &frame,
2036 xpos, xpos + overlay->image_width,
2037 ypos, ypos + overlay->image_height);
2039 case GST_VIDEO_FORMAT_BGRA:
2040 gst_base_text_overlay_shade_BGRA (overlay, &frame,
2041 xpos, xpos + overlay->image_width,
2042 ypos, ypos + overlay->image_height);
2045 g_assert_not_reached ();
2052 if (overlay->text_image) {
2053 switch (overlay->format) {
2054 case GST_VIDEO_FORMAT_I420:
2055 gst_base_text_overlay_blit_I420 (overlay, &frame, xpos, ypos);
2057 case GST_VIDEO_FORMAT_NV12:
2058 case GST_VIDEO_FORMAT_NV21:
2059 gst_base_text_overlay_blit_NV12_NV21 (overlay, &frame, xpos, ypos);
2061 case GST_VIDEO_FORMAT_UYVY:
2062 gst_base_text_overlay_blit_UYVY (overlay, &frame, xpos, ypos);
2064 case GST_VIDEO_FORMAT_AYUV:
2065 gst_base_text_overlay_blit_AYUV (overlay, &frame, xpos, ypos);
2067 case GST_VIDEO_FORMAT_BGRx:
2068 gst_base_text_overlay_blit_BGRx (overlay, &frame, xpos, ypos);
2070 case GST_VIDEO_FORMAT_xRGB:
2071 gst_base_text_overlay_blit_xRGB (overlay, &frame, xpos, ypos);
2073 case GST_VIDEO_FORMAT_RGBx:
2074 gst_base_text_overlay_blit_RGBx (overlay, &frame, xpos, ypos);
2076 case GST_VIDEO_FORMAT_xBGR:
2077 gst_base_text_overlay_blit_xBGR (overlay, &frame, xpos, ypos);
2079 case GST_VIDEO_FORMAT_ARGB:
2080 gst_base_text_overlay_blit_ARGB (overlay, &frame, xpos, ypos);
2082 case GST_VIDEO_FORMAT_ABGR:
2083 gst_base_text_overlay_blit_ABGR (overlay, &frame, xpos, ypos);
2085 case GST_VIDEO_FORMAT_RGBA:
2086 gst_base_text_overlay_blit_RGBA (overlay, &frame, xpos, ypos);
2088 case GST_VIDEO_FORMAT_BGRA:
2089 gst_base_text_overlay_blit_BGRA (overlay, &frame, xpos, ypos);
2092 g_assert_not_reached ();
2095 gst_video_frame_unmap (&frame);
2097 return gst_pad_push (overlay->srcpad, video_frame);
2102 GST_DEBUG_OBJECT (overlay, "received invalid buffer");
2107 static GstPadLinkReturn
2108 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2110 GstBaseTextOverlay *overlay;
2112 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2113 if (G_UNLIKELY (!overlay))
2114 return GST_PAD_LINK_REFUSED;
2116 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2118 overlay->text_linked = TRUE;
2120 gst_object_unref (overlay);
2122 return GST_PAD_LINK_OK;
2126 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2128 GstBaseTextOverlay *overlay;
2130 /* don't use gst_pad_get_parent() here, will deadlock */
2131 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2133 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2135 overlay->text_linked = FALSE;
2137 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2141 gst_base_text_overlay_text_event (GstPad * pad, GstEvent * event)
2143 gboolean ret = FALSE;
2144 GstBaseTextOverlay *overlay = NULL;
2146 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2147 if (G_UNLIKELY (!overlay)) {
2148 gst_event_unref (event);
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 (overlay, 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));
2241 if (G_UNLIKELY (!overlay)) {
2242 gst_event_unref (event);
2246 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2248 switch (GST_EVENT_TYPE (event)) {
2249 case GST_EVENT_CAPS:
2253 gst_event_parse_caps (event, &caps);
2254 ret = gst_base_text_overlay_setcaps (overlay, caps);
2255 gst_event_unref (event);
2258 case GST_EVENT_SEGMENT:
2260 const GstSegment *segment;
2262 GST_DEBUG_OBJECT (overlay, "received new segment");
2264 gst_event_parse_segment (event, &segment);
2266 if (segment->format == GST_FORMAT_TIME) {
2267 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2270 gst_segment_copy_into (segment, &overlay->segment);
2272 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2273 ("received non-TIME newsegment event on video input"));
2276 ret = gst_pad_event_default (pad, event);
2280 GST_OBJECT_LOCK (overlay);
2281 GST_INFO_OBJECT (overlay, "video EOS");
2282 overlay->video_eos = TRUE;
2283 GST_OBJECT_UNLOCK (overlay);
2284 ret = gst_pad_event_default (pad, event);
2286 case GST_EVENT_FLUSH_START:
2287 GST_OBJECT_LOCK (overlay);
2288 GST_INFO_OBJECT (overlay, "video flush start");
2289 overlay->video_flushing = TRUE;
2290 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2291 GST_OBJECT_UNLOCK (overlay);
2292 ret = gst_pad_event_default (pad, event);
2294 case GST_EVENT_FLUSH_STOP:
2295 GST_OBJECT_LOCK (overlay);
2296 GST_INFO_OBJECT (overlay, "video flush stop");
2297 overlay->video_flushing = FALSE;
2298 overlay->video_eos = FALSE;
2299 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2300 GST_OBJECT_UNLOCK (overlay);
2301 ret = gst_pad_event_default (pad, event);
2304 ret = gst_pad_event_default (pad, event);
2308 gst_object_unref (overlay);
2313 /* Called with lock held */
2315 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2317 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2319 if (overlay->text_buffer) {
2320 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2321 overlay->text_buffer);
2322 gst_buffer_unref (overlay->text_buffer);
2323 overlay->text_buffer = NULL;
2326 /* Let the text task know we used that buffer */
2327 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2330 /* We receive text buffers here. If they are out of segment we just ignore them.
2331 If the buffer is in our segment we keep it internally except if another one
2332 is already waiting here, in that case we wait that it gets kicked out */
2333 static GstFlowReturn
2334 gst_base_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
2336 GstFlowReturn ret = GST_FLOW_OK;
2337 GstBaseTextOverlay *overlay = NULL;
2338 gboolean in_seg = FALSE;
2339 guint64 clip_start = 0, clip_stop = 0;
2341 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2343 GST_OBJECT_LOCK (overlay);
2345 if (overlay->text_flushing) {
2346 GST_OBJECT_UNLOCK (overlay);
2347 ret = GST_FLOW_WRONG_STATE;
2348 GST_LOG_OBJECT (overlay, "text flushing");
2352 if (overlay->text_eos) {
2353 GST_OBJECT_UNLOCK (overlay);
2355 GST_LOG_OBJECT (overlay, "text EOS");
2359 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2360 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2361 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2362 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2363 GST_BUFFER_DURATION (buffer)));
2365 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2368 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2369 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2371 stop = GST_CLOCK_TIME_NONE;
2373 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2374 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2380 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2381 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2382 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2383 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2385 /* Wait for the previous buffer to go away */
2386 while (overlay->text_buffer != NULL) {
2387 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2388 GST_DEBUG_PAD_NAME (pad));
2389 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2390 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2391 if (overlay->text_flushing) {
2392 GST_OBJECT_UNLOCK (overlay);
2393 ret = GST_FLOW_WRONG_STATE;
2398 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2399 overlay->text_segment.position = clip_start;
2401 overlay->text_buffer = buffer;
2402 /* That's a new text buffer we need to render */
2403 overlay->need_render = TRUE;
2405 /* in case the video chain is waiting for a text buffer, wake it up */
2406 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2409 GST_OBJECT_UNLOCK (overlay);
2416 static GstFlowReturn
2417 gst_base_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
2419 GstBaseTextOverlayClass *klass;
2420 GstBaseTextOverlay *overlay;
2421 GstFlowReturn ret = GST_FLOW_OK;
2422 gboolean in_seg = FALSE;
2423 guint64 start, stop, clip_start = 0, clip_stop = 0;
2426 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2427 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2429 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2430 goto missing_timestamp;
2432 /* ignore buffers that are outside of the current segment */
2433 start = GST_BUFFER_TIMESTAMP (buffer);
2435 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2436 stop = GST_CLOCK_TIME_NONE;
2438 stop = start + GST_BUFFER_DURATION (buffer);
2441 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2442 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2443 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2445 /* segment_clip() will adjust start unconditionally to segment_start if
2446 * no stop time is provided, so handle this ourselves */
2447 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2448 goto out_of_segment;
2450 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2451 &clip_start, &clip_stop);
2454 goto out_of_segment;
2456 /* if the buffer is only partially in the segment, fix up stamps */
2457 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2458 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2459 buffer = gst_buffer_make_writable (buffer);
2460 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2462 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2465 /* now, after we've done the clipping, fix up end time if there's no
2466 * duration (we only use those estimated values internally though, we
2467 * don't want to set bogus values on the buffer itself) */
2471 gint fps_num, fps_denom;
2473 /* FIXME, store this in setcaps */
2474 caps = gst_pad_get_current_caps (pad);
2475 s = gst_caps_get_structure (caps, 0);
2476 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2477 fps_num && fps_denom) {
2478 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2479 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2481 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2482 stop = start + 1; /* we need to assume some interval */
2484 gst_caps_unref (caps);
2487 gst_object_sync_values (G_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2491 GST_OBJECT_LOCK (overlay);
2493 if (overlay->video_flushing)
2496 if (overlay->video_eos)
2499 if (overlay->silent) {
2500 GST_OBJECT_UNLOCK (overlay);
2501 ret = gst_pad_push (overlay->srcpad, buffer);
2503 /* Update position */
2504 overlay->segment.position = clip_start;
2509 /* Text pad not linked, rendering internal text */
2510 if (!overlay->text_linked) {
2511 if (klass->get_text) {
2512 text = klass->get_text (overlay, buffer);
2514 text = g_strdup (overlay->default_text);
2517 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2518 "text: '%s'", GST_STR_NULL (text));
2520 GST_OBJECT_UNLOCK (overlay);
2522 if (text != NULL && *text != '\0') {
2523 /* Render and push */
2524 gst_base_text_overlay_render_text (overlay, text, -1);
2525 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2527 /* Invalid or empty string */
2528 ret = gst_pad_push (overlay->srcpad, buffer);
2531 /* Text pad linked, check if we have a text buffer queued */
2532 if (overlay->text_buffer) {
2533 gboolean pop_text = FALSE, valid_text_time = TRUE;
2534 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2535 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2536 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2537 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2538 GstClockTime vid_running_time, vid_running_time_end;
2540 /* if the text buffer isn't stamped right, pop it off the
2541 * queue and display it for the current video frame only */
2542 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2543 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2544 GST_WARNING_OBJECT (overlay,
2545 "Got text buffer with invalid timestamp or duration");
2547 valid_text_time = FALSE;
2549 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2550 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2554 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2556 vid_running_time_end =
2557 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2560 /* If timestamp and duration are valid */
2561 if (valid_text_time) {
2563 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2565 text_running_time_end =
2566 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2570 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2571 GST_TIME_ARGS (text_running_time),
2572 GST_TIME_ARGS (text_running_time_end));
2573 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2574 GST_TIME_ARGS (vid_running_time),
2575 GST_TIME_ARGS (vid_running_time_end));
2577 /* Text too old or in the future */
2578 if (valid_text_time && text_running_time_end <= vid_running_time) {
2579 /* text buffer too old, get rid of it and do nothing */
2580 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2582 gst_base_text_overlay_pop_text (overlay);
2583 GST_OBJECT_UNLOCK (overlay);
2584 goto wait_for_text_buf;
2585 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2586 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2587 GST_OBJECT_UNLOCK (overlay);
2588 /* Push the video frame */
2589 ret = gst_pad_push (overlay->srcpad, buffer);
2591 gchar *in_text, *otext;
2592 gsize in_size, osize;
2595 gst_buffer_map (overlay->text_buffer, &osize, NULL, GST_MAP_READ);
2599 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2600 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2601 * here on purpose, this is something that needs fixing upstream */
2602 if (!g_utf8_validate (in_text, in_size, NULL)) {
2603 const gchar *end = NULL;
2605 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2606 in_text = g_strndup (in_text, in_size);
2607 while (!g_utf8_validate (in_text, in_size, &end) && end)
2608 *((gchar *) end) = '*';
2611 /* Get the string */
2612 if (overlay->have_pango_markup) {
2613 text = g_strndup (in_text, in_size);
2615 text = g_markup_escape_text (in_text, in_size);
2618 if (text != NULL && *text != '\0') {
2619 gint text_len = strlen (text);
2621 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2622 text[text_len - 1] == '\r')) {
2625 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2626 gst_base_text_overlay_render_text (overlay, text, text_len);
2628 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2629 gst_base_text_overlay_render_text (overlay, " ", 1);
2631 gst_buffer_unmap (overlay->text_buffer, otext, osize);
2633 if (in_text != otext)
2636 GST_OBJECT_UNLOCK (overlay);
2637 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2639 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2640 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2645 GST_OBJECT_LOCK (overlay);
2646 gst_base_text_overlay_pop_text (overlay);
2647 GST_OBJECT_UNLOCK (overlay);
2650 gboolean wait_for_text_buf = TRUE;
2652 if (overlay->text_eos)
2653 wait_for_text_buf = FALSE;
2655 if (!overlay->wait_text)
2656 wait_for_text_buf = FALSE;
2658 /* Text pad linked, but no text buffer available - what now? */
2659 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2660 GstClockTime text_start_running_time, text_position_running_time;
2661 GstClockTime vid_running_time;
2664 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2665 GST_BUFFER_TIMESTAMP (buffer));
2666 text_start_running_time =
2667 gst_segment_to_running_time (&overlay->text_segment,
2668 GST_FORMAT_TIME, overlay->text_segment.start);
2669 text_position_running_time =
2670 gst_segment_to_running_time (&overlay->text_segment,
2671 GST_FORMAT_TIME, overlay->text_segment.position);
2673 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2674 vid_running_time < text_start_running_time) ||
2675 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2676 vid_running_time < text_position_running_time)) {
2677 wait_for_text_buf = FALSE;
2681 if (wait_for_text_buf) {
2682 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2683 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2684 GST_DEBUG_OBJECT (overlay, "resuming");
2685 GST_OBJECT_UNLOCK (overlay);
2686 goto wait_for_text_buf;
2688 GST_OBJECT_UNLOCK (overlay);
2689 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2690 ret = gst_pad_push (overlay->srcpad, buffer);
2697 /* Update position */
2698 overlay->segment.position = clip_start;
2704 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2705 gst_buffer_unref (buffer);
2711 GST_OBJECT_UNLOCK (overlay);
2712 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2713 gst_buffer_unref (buffer);
2714 return GST_FLOW_WRONG_STATE;
2718 GST_OBJECT_UNLOCK (overlay);
2719 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2720 gst_buffer_unref (buffer);
2721 return GST_FLOW_EOS;
2725 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2726 gst_buffer_unref (buffer);
2731 static GstStateChangeReturn
2732 gst_base_text_overlay_change_state (GstElement * element,
2733 GstStateChange transition)
2735 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2736 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2738 switch (transition) {
2739 case GST_STATE_CHANGE_PAUSED_TO_READY:
2740 GST_OBJECT_LOCK (overlay);
2741 overlay->text_flushing = TRUE;
2742 overlay->video_flushing = TRUE;
2743 /* pop_text will broadcast on the GCond and thus also make the video
2744 * chain exit if it's waiting for a text buffer */
2745 gst_base_text_overlay_pop_text (overlay);
2746 GST_OBJECT_UNLOCK (overlay);
2752 ret = parent_class->change_state (element, transition);
2753 if (ret == GST_STATE_CHANGE_FAILURE)
2756 switch (transition) {
2757 case GST_STATE_CHANGE_READY_TO_PAUSED:
2758 GST_OBJECT_LOCK (overlay);
2759 overlay->text_flushing = FALSE;
2760 overlay->video_flushing = FALSE;
2761 overlay->video_eos = FALSE;
2762 overlay->text_eos = FALSE;
2763 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2764 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2765 GST_OBJECT_UNLOCK (overlay);
2775 plugin_init (GstPlugin * plugin)
2777 gst_controller_init (NULL, NULL);
2779 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2780 GST_TYPE_TEXT_OVERLAY) ||
2781 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2782 GST_TYPE_TIME_OVERLAY) ||
2783 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2784 GST_TYPE_CLOCK_OVERLAY) ||
2785 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2786 GST_TYPE_TEXT_RENDER)) {
2790 /*texttestsrc_plugin_init(module, plugin); */
2792 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2797 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2798 "pango", "Pango-based text rendering and overlay", plugin_init,
2799 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)