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 gboolean gst_base_text_overlay_video_query (GstPad * pad,
324 static GstFlowReturn gst_base_text_overlay_video_chain (GstPad * pad,
327 static gboolean gst_base_text_overlay_text_event (GstPad * pad,
329 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
331 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
333 static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
334 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
335 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
338 static void gst_base_text_overlay_finalize (GObject * object);
339 static void gst_base_text_overlay_set_property (GObject * object, guint prop_id,
340 const GValue * value, GParamSpec * pspec);
341 static void gst_base_text_overlay_get_property (GObject * object, guint prop_id,
342 GValue * value, GParamSpec * pspec);
344 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
345 PangoFontDescription * desc);
348 gst_base_text_overlay_get_type (void)
350 static GType type = 0;
352 if (g_once_init_enter ((gsize *) & type)) {
353 static const GTypeInfo info = {
354 sizeof (GstBaseTextOverlayClass),
355 (GBaseInitFunc) gst_base_text_overlay_base_init,
357 (GClassInitFunc) gst_base_text_overlay_class_init,
360 sizeof (GstBaseTextOverlay),
362 (GInstanceInitFunc) gst_base_text_overlay_init,
365 g_once_init_leave ((gsize *) & type,
366 g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTextOverlay", &info,
374 gst_base_text_overlay_get_text (GstBaseTextOverlay * overlay,
375 GstBuffer * video_frame)
377 return g_strdup (overlay->default_text);
381 gst_base_text_overlay_base_init (gpointer g_class)
383 GstBaseTextOverlayClass *klass = GST_BASE_TEXT_OVERLAY_CLASS (g_class);
384 PangoFontMap *fontmap;
386 /* Only lock for the subclasses here, the base class
387 * doesn't have this mutex yet and it's not necessary
389 if (klass->pango_lock)
390 g_mutex_lock (klass->pango_lock);
391 fontmap = pango_cairo_font_map_get_default ();
392 klass->pango_context =
393 pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
394 if (klass->pango_lock)
395 g_mutex_unlock (klass->pango_lock);
399 gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
401 GObjectClass *gobject_class;
402 GstElementClass *gstelement_class;
404 gobject_class = (GObjectClass *) klass;
405 gstelement_class = (GstElementClass *) klass;
407 parent_class = g_type_class_peek_parent (klass);
409 gobject_class->finalize = gst_base_text_overlay_finalize;
410 gobject_class->set_property = gst_base_text_overlay_set_property;
411 gobject_class->get_property = gst_base_text_overlay_get_property;
413 gst_element_class_add_pad_template (gstelement_class,
414 gst_static_pad_template_get (&src_template_factory));
415 gst_element_class_add_pad_template (gstelement_class,
416 gst_static_pad_template_get (&video_sink_template_factory));
418 gstelement_class->change_state =
419 GST_DEBUG_FUNCPTR (gst_base_text_overlay_change_state);
421 klass->pango_lock = g_mutex_new ();
423 klass->get_text = gst_base_text_overlay_get_text;
425 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
426 g_param_spec_string ("text", "text",
427 "Text to be display.", DEFAULT_PROP_TEXT,
428 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
429 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
430 g_param_spec_boolean ("shaded-background", "shaded background",
431 "Whether to shade the background under the text area",
432 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
433 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
434 g_param_spec_enum ("valignment", "vertical alignment",
435 "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
436 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
437 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
438 g_param_spec_enum ("halignment", "horizontal alignment",
439 "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
440 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
441 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
442 g_param_spec_string ("valign", "vertical alignment",
443 "Vertical alignment of the text (deprecated; use valignment)",
444 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
445 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
446 g_param_spec_string ("halign", "horizontal alignment",
447 "Horizontal alignment of the text (deprecated; use halignment)",
448 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
449 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
450 g_param_spec_int ("xpad", "horizontal paddding",
451 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
452 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
453 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
454 g_param_spec_int ("ypad", "vertical padding",
455 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
456 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
457 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
458 g_param_spec_int ("deltax", "X position modifier",
459 "Shift X position to the left or to the right. Unit is pixels.",
460 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
461 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
462 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
463 g_param_spec_int ("deltay", "Y position modifier",
464 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
465 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
467 * GstBaseTextOverlay:xpos
469 * Horizontal position of the rendered text when using positioned alignment.
473 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
474 g_param_spec_double ("xpos", "horizontal position",
475 "Horizontal position when using position alignment", 0, 1.0,
477 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
479 * GstBaseTextOverlay:ypos
481 * Vertical position of the rendered text when using positioned alignment.
485 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
486 g_param_spec_double ("ypos", "vertical position",
487 "Vertical position when using position alignment", 0, 1.0,
489 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
490 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
491 g_param_spec_enum ("wrap-mode", "wrap mode",
492 "Whether to wrap the text and if so how.",
493 GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
494 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
495 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
496 g_param_spec_string ("font-desc", "font description",
497 "Pango font description of font to be used for rendering. "
498 "See documentation of pango_font_description_from_string "
499 "for syntax.", DEFAULT_PROP_FONT_DESC,
500 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
502 * GstBaseTextOverlay:color
504 * Color of the rendered text.
508 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
509 g_param_spec_uint ("color", "Color",
510 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
512 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
514 * GstTextOverlay:outline-color
516 * Color of the outline of the rendered text.
520 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
521 g_param_spec_uint ("outline-color", "Text Outline Color",
522 "Color to use for outline the text (big-endian ARGB).", 0,
523 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
524 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
527 * GstBaseTextOverlay:line-alignment
529 * Alignment of text lines relative to each other (for multi-line text)
533 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
534 g_param_spec_enum ("line-alignment", "line alignment",
535 "Alignment of text lines relative to each other.",
536 GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
537 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
539 * GstBaseTextOverlay:silent
541 * If set, no text is rendered. Useful to switch off text rendering
542 * temporarily without removing the textoverlay element from the pipeline.
546 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
547 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
548 g_param_spec_boolean ("silent", "silent",
549 "Whether to render the text string",
551 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
553 * GstBaseTextOverlay:wait-text
555 * If set, the video will block until a subtitle is received on the text pad.
556 * If video and subtitles are sent in sync, like from the same demuxer, this
557 * property should be set.
561 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
562 g_param_spec_boolean ("wait-text", "Wait Text",
563 "Whether to wait for subtitles",
564 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
566 g_object_class_install_property (G_OBJECT_CLASS (klass),
567 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
568 "Automatically adjust font size to screen-size.",
569 DEFAULT_PROP_AUTO_ADJUST_SIZE,
570 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
572 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
573 g_param_spec_boolean ("vertical-render", "vertical render",
574 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
575 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
579 gst_base_text_overlay_finalize (GObject * object)
581 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
583 g_free (overlay->default_text);
585 if (overlay->text_image) {
586 g_free (overlay->text_image);
587 overlay->text_image = NULL;
590 if (overlay->layout) {
591 g_object_unref (overlay->layout);
592 overlay->layout = NULL;
595 if (overlay->text_buffer) {
596 gst_buffer_unref (overlay->text_buffer);
597 overlay->text_buffer = NULL;
601 g_cond_free (overlay->cond);
602 overlay->cond = NULL;
605 G_OBJECT_CLASS (parent_class)->finalize (object);
609 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
610 GstBaseTextOverlayClass * klass)
612 GstPadTemplate *template;
613 PangoFontDescription *desc;
616 template = gst_static_pad_template_get (&video_sink_template_factory);
617 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
618 gst_object_unref (template);
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_pad_set_query_function (overlay->video_sinkpad,
624 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_query));
625 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
628 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
632 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
633 gst_object_unref (template);
635 gst_pad_set_event_function (overlay->text_sinkpad,
636 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
637 gst_pad_set_chain_function (overlay->text_sinkpad,
638 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
639 gst_pad_set_link_function (overlay->text_sinkpad,
640 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
641 gst_pad_set_unlink_function (overlay->text_sinkpad,
642 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
643 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
647 template = gst_static_pad_template_get (&src_template_factory);
648 overlay->srcpad = gst_pad_new_from_template (template, "src");
649 gst_object_unref (template);
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 switch (GST_QUERY_TYPE (query)) {
1006 case GST_QUERY_CAPS:
1008 GstCaps *filter, *caps;
1010 gst_query_parse_caps (query, &filter);
1011 caps = gst_base_text_overlay_getcaps (pad, filter);
1012 gst_query_set_caps_result (query, caps);
1013 gst_caps_unref (caps);
1018 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1022 gst_object_unref (overlay);
1028 gst_base_text_overlay_src_event (GstPad * pad, GstEvent * event)
1030 gboolean ret = FALSE;
1031 GstBaseTextOverlay *overlay = NULL;
1033 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1034 if (G_UNLIKELY (!overlay)) {
1035 gst_event_unref (event);
1039 switch (GST_EVENT_TYPE (event)) {
1040 case GST_EVENT_SEEK:{
1043 /* We don't handle seek if we have not text pad */
1044 if (!overlay->text_linked) {
1045 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1046 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1050 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1052 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1054 /* Flush downstream, only for flushing seek */
1055 if (flags & GST_SEEK_FLAG_FLUSH)
1056 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1058 /* Mark ourself as flushing, unblock chains */
1059 GST_OBJECT_LOCK (overlay);
1060 overlay->video_flushing = TRUE;
1061 overlay->text_flushing = TRUE;
1062 gst_base_text_overlay_pop_text (overlay);
1063 GST_OBJECT_UNLOCK (overlay);
1065 /* Seek on each sink pad */
1066 gst_event_ref (event);
1067 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1069 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1071 gst_event_unref (event);
1076 if (overlay->text_linked) {
1077 gst_event_ref (event);
1078 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1079 gst_pad_push_event (overlay->text_sinkpad, event);
1081 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1087 gst_object_unref (overlay);
1093 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1095 GstBaseTextOverlay *overlay;
1099 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1100 if (G_UNLIKELY (!overlay))
1101 return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1103 if (pad == overlay->srcpad)
1104 otherpad = overlay->video_sinkpad;
1106 otherpad = overlay->srcpad;
1108 /* we can do what the peer can */
1109 caps = gst_pad_peer_query_caps (otherpad, filter);
1111 GstCaps *temp, *templ;
1113 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1115 /* filtered against our padtemplate */
1116 templ = gst_pad_get_pad_template_caps (otherpad);
1117 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1118 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1119 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1120 gst_caps_unref (caps);
1121 gst_caps_unref (templ);
1122 /* this is what we can do */
1125 /* no peer, our padtemplate is enough then */
1126 caps = gst_pad_get_pad_template_caps (pad);
1128 GstCaps *intersection;
1131 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1132 gst_caps_unref (caps);
1133 caps = intersection;
1137 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1139 gst_object_unref (overlay);
1145 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1146 PangoFontDescription * desc)
1148 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1149 overlay->shadow_offset = (double) (font_size) / 13.0;
1150 overlay->outline_offset = (double) (font_size) / 15.0;
1151 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1152 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1155 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1156 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1157 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1158 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1162 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1163 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1170 gint width = overlay->image_width;
1171 gint height = overlay->image_height;
1177 if (xpos + width > overlay->width) {
1178 width = overlay->width - xpos;
1181 if (ypos + height > overlay->height) {
1182 height = overlay->height - ypos;
1185 dest += (ypos / 1) * dest_stride;
1187 for (i = 0; i < height; i++) {
1188 pimage = text_image + 4 * (i * overlay->image_width);
1189 py = dest + i * dest_stride + xpos;
1190 for (j = 0; j < width; j++) {
1191 b = pimage[CAIRO_ARGB_B];
1192 g = pimage[CAIRO_ARGB_G];
1193 r = pimage[CAIRO_ARGB_R];
1194 a = pimage[CAIRO_ARGB_A];
1195 CAIRO_UNPREMULTIPLY (a, r, g, b);
1202 COMP_Y (y, r, g, b);
1204 BLEND (*py++, a, y, x);
1210 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1211 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1212 guint destcb_stride, guint destcr_stride, guint pix_stride)
1217 gushort r1, g1, b1, a1;
1218 guchar *pimage1, *pimage2;
1220 gint width = overlay->image_width - 2;
1221 gint height = overlay->image_height - 2;
1229 if (xpos + width > overlay->width) {
1230 width = overlay->width - xpos;
1233 if (ypos + height > overlay->height) {
1234 height = overlay->height - ypos;
1237 destcb += (ypos / 2) * destcb_stride;
1238 destcr += (ypos / 2) * destcr_stride;
1240 for (i = 0; i < height; i += 2) {
1241 pimage1 = text_image + 4 * (i * overlay->image_width);
1242 pimage2 = pimage1 + 4 * overlay->image_width;
1243 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1244 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1245 for (j = 0; j < width; j += 2) {
1246 b = pimage1[CAIRO_ARGB_B];
1247 g = pimage1[CAIRO_ARGB_G];
1248 r = pimage1[CAIRO_ARGB_R];
1249 a = pimage1[CAIRO_ARGB_A];
1250 CAIRO_UNPREMULTIPLY (a, r, g, b);
1253 b1 = pimage1[CAIRO_ARGB_B];
1254 g1 = pimage1[CAIRO_ARGB_G];
1255 r1 = pimage1[CAIRO_ARGB_R];
1256 a1 = pimage1[CAIRO_ARGB_A];
1257 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1264 b1 = pimage2[CAIRO_ARGB_B];
1265 g1 = pimage2[CAIRO_ARGB_G];
1266 r1 = pimage2[CAIRO_ARGB_R];
1267 a1 = pimage2[CAIRO_ARGB_A];
1268 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1275 /* + 2 for rounding */
1276 b1 = pimage2[CAIRO_ARGB_B];
1277 g1 = pimage2[CAIRO_ARGB_G];
1278 r1 = pimage2[CAIRO_ARGB_R];
1279 a1 = pimage2[CAIRO_ARGB_A];
1280 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1297 COMP_U (cb, r, g, b);
1298 COMP_V (cr, r, g, b);
1301 BLEND (*pcb, a, cb, x);
1303 BLEND (*pcr, a, cr, x);
1312 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1313 const gchar * string, gint textlen)
1316 cairo_surface_t *surface;
1317 PangoRectangle ink_rect, logical_rect;
1318 cairo_matrix_t cairo_matrix;
1320 double scalef = 1.0;
1323 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1325 if (overlay->auto_adjust_size) {
1326 /* 640 pixel is default */
1327 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1329 pango_layout_set_width (overlay->layout, -1);
1330 /* set text on pango layout */
1331 pango_layout_set_markup (overlay->layout, string, textlen);
1333 /* get subtitle image size */
1334 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1336 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1338 if (width + overlay->deltax >
1339 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1341 * subtitle image width is larger then overlay width
1342 * so rearrange overlay wrap mode.
1344 gst_base_text_overlay_update_wrap_mode (overlay);
1345 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1346 width = overlay->width;
1350 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1351 if (height > overlay->height) {
1352 height = overlay->height;
1354 if (overlay->use_vertical_render) {
1355 PangoRectangle rect;
1356 PangoContext *context;
1357 PangoMatrix matrix = PANGO_MATRIX_INIT;
1360 context = pango_layout_get_context (overlay->layout);
1362 pango_matrix_rotate (&matrix, -90);
1364 rect.x = rect.y = 0;
1366 rect.height = height;
1367 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1368 matrix.x0 = -rect.x;
1369 matrix.y0 = -rect.y;
1371 pango_context_set_matrix (context, &matrix);
1373 cairo_matrix.xx = matrix.xx;
1374 cairo_matrix.yx = matrix.yx;
1375 cairo_matrix.xy = matrix.xy;
1376 cairo_matrix.yy = matrix.yy;
1377 cairo_matrix.x0 = matrix.x0;
1378 cairo_matrix.y0 = matrix.y0;
1379 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1385 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1388 /* reallocate surface */
1389 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1391 surface = cairo_image_surface_create_for_data (overlay->text_image,
1392 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1393 cr = cairo_create (surface);
1396 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1399 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1401 if (overlay->want_shading)
1402 cairo_paint_with_alpha (cr, overlay->shading_value);
1404 /* apply transformations */
1405 cairo_set_matrix (cr, &cairo_matrix);
1407 /* FIXME: We use show_layout everywhere except for the surface
1408 * because it's really faster and internally does all kinds of
1409 * caching. Unfortunately we have to paint to a cairo path for
1410 * the outline and this is slow. Once Pango supports user fonts
1411 * we should use them, see
1412 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1414 * Idea would the be, to create a cairo user font that
1415 * does shadow, outline, text painting in the
1416 * render_glyph function.
1419 /* draw shadow text */
1421 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1422 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1423 pango_cairo_show_layout (cr, overlay->layout);
1426 a = (overlay->outline_color >> 24) & 0xff;
1427 r = (overlay->outline_color >> 16) & 0xff;
1428 g = (overlay->outline_color >> 8) & 0xff;
1429 b = (overlay->outline_color >> 0) & 0xff;
1431 /* draw outline text */
1433 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1434 cairo_set_line_width (cr, overlay->outline_offset);
1435 pango_cairo_layout_path (cr, overlay->layout);
1439 a = (overlay->color >> 24) & 0xff;
1440 r = (overlay->color >> 16) & 0xff;
1441 g = (overlay->color >> 8) & 0xff;
1442 b = (overlay->color >> 0) & 0xff;
1446 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1447 pango_cairo_show_layout (cr, overlay->layout);
1451 cairo_surface_destroy (surface);
1452 overlay->image_width = width;
1453 overlay->image_height = height;
1454 overlay->baseline_y = ink_rect.y;
1455 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1462 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1463 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1465 gint i, j, dest_stride;
1468 dest_stride = dest->info.stride[0];
1469 dest_ptr = dest->data[0];
1471 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1472 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1474 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1475 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1477 for (i = y0; i < y1; ++i) {
1478 for (j = x0; j < x1; ++j) {
1479 gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
1481 dest_ptr[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1487 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1488 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1491 guint dest_stride, pixel_stride;
1494 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
1495 dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
1496 pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
1498 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1499 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1501 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1502 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1505 x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
1507 x1 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x1);
1510 y0 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y0);
1512 y1 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y1);
1514 for (i = y0; i < y1; i++) {
1515 for (j = x0; j < x1; j++) {
1519 y_pos = (i * dest_stride) + j * pixel_stride;
1520 y = dest_ptr[y_pos] + overlay->shading_value;
1522 dest_ptr[y_pos] = CLAMP (y, 0, 255);
1527 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1528 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1529 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1531 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
1532 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1537 dest_ptr = dest->data[0];
1539 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1540 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1542 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1543 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1545 for (i = y0; i < y1; i++) {
1546 for (j = x0; j < x1; j++) {
1549 y_pos = (i * 4 * overlay->width) + j * 4;
1550 for (k = 0; k < 4; k++) {
1551 y = dest_ptr[y_pos + k] + overlay->shading_value;
1552 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);
1558 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1559 static inline void \
1560 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
1561 gint x0, gint x1, gint y0, gint y1) \
1566 dest_ptr = dest->data[0];\
1568 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1569 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1571 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1572 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1574 for (i = y0; i < y1; i++) {\
1575 for (j = x0; j < x1; j++) {\
1577 y_pos = (i * 4 * overlay->width) + j * 4;\
1578 for (k = OFFSET; k < 3+OFFSET; k++) {\
1579 y = dest_ptr[y_pos + k] + overlay->shading_value;\
1580 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);\
1585 ARGB_SHADE_FUNCTION (ARGB, 1);
1586 ARGB_SHADE_FUNCTION (ABGR, 1);
1587 ARGB_SHADE_FUNCTION (RGBA, 0);
1588 ARGB_SHADE_FUNCTION (BGRA, 0);
1592 * - use proper strides and offset for I420
1593 * - don't draw over the edge of the picture (try a longer
1594 * text with a huge font size)
1598 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1599 GstVideoFrame * dest, gint xpos, gint ypos)
1601 int y_stride, u_stride, v_stride;
1602 guint8 *y_pixels, *u_pixels, *v_pixels;
1604 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1605 * to a boundary of integer number of U/V pixels:
1607 xpos = GST_ROUND_UP_2 (xpos);
1608 ypos = GST_ROUND_UP_2 (ypos);
1610 y_pixels = dest->data[0];
1611 u_pixels = dest->data[1];
1612 v_pixels = dest->data[2];
1613 y_stride = dest->info.stride[0];
1614 u_stride = dest->info.stride[1];
1615 v_stride = dest->info.stride[2];
1617 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1618 overlay->text_image, y_stride);
1619 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1620 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 2);
1624 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1625 GstVideoFrame * dest, gint xpos, gint ypos)
1627 int y_stride, u_stride, v_stride;
1628 guint8 *y_pixels, *u_pixels, *v_pixels;
1630 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1631 * to a boundary of integer number of U/V pixels:
1633 xpos = GST_ROUND_UP_2 (xpos);
1634 ypos = GST_ROUND_UP_2 (ypos);
1636 y_pixels = dest->data[0];
1637 u_pixels = dest->data[1];
1638 v_pixels = dest->data[2];
1639 y_stride = dest->info.stride[0];
1640 u_stride = dest->info.stride[1];
1641 v_stride = dest->info.stride[2];
1643 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1644 overlay->text_image, y_stride);
1645 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1646 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 1);
1650 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1651 GstVideoFrame * dest, gint xpos, gint ypos)
1658 guchar *pimage, *dest_ptr;
1661 yuv_pixels = dest->data[0];
1663 /* because U/V is 2x horizontally subsampled, we need to round to a
1664 * boundary of integer number of U/V pixels in x dimension:
1666 xpos = GST_ROUND_UP_2 (xpos);
1668 w = overlay->image_width - 2;
1669 h = overlay->image_height - 2;
1675 if (xpos + w > overlay->width) {
1676 w = overlay->width - xpos;
1679 if (ypos + h > overlay->height) {
1680 h = overlay->height - ypos;
1683 for (i = 0; i < h; i++) {
1684 pimage = overlay->text_image + i * overlay->image_width * 4;
1685 dest_ptr = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1686 for (j = 0; j < w; j += 2) {
1687 b0 = pimage[CAIRO_ARGB_B];
1688 g0 = pimage[CAIRO_ARGB_G];
1689 r0 = pimage[CAIRO_ARGB_R];
1690 a0 = pimage[CAIRO_ARGB_A];
1691 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1694 b1 = pimage[CAIRO_ARGB_B];
1695 g1 = pimage[CAIRO_ARGB_G];
1696 r1 = pimage[CAIRO_ARGB_R];
1697 a1 = pimage[CAIRO_ARGB_A];
1698 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1708 COMP_Y (y0, r0, g0, b0);
1709 COMP_Y (y1, r1, g1, b1);
1719 COMP_U (u, r0, g0, b0);
1720 COMP_V (v, r0, g0, b0);
1722 BLEND (*dest_ptr, a0, u, *dest_ptr);
1724 BLEND (*dest_ptr, a0, y0, *dest_ptr);
1726 BLEND (*dest_ptr, a0, v, *dest_ptr);
1728 BLEND (*dest_ptr, a0, y1, *dest_ptr);
1735 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1736 GstVideoFrame * dest, gint xpos, gint ypos)
1742 guchar *pimage, *dest_ptr;
1745 rgb_pixels = dest->data[0];
1747 w = overlay->image_width;
1748 h = overlay->image_height;
1754 if (xpos + w > overlay->width) {
1755 w = overlay->width - xpos;
1758 if (ypos + h > overlay->height) {
1759 h = overlay->height - ypos;
1762 for (i = 0; i < h; i++) {
1763 pimage = overlay->text_image + i * overlay->image_width * 4;
1764 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1765 for (j = 0; j < w; j++) {
1766 a = pimage[CAIRO_ARGB_A];
1767 b = pimage[CAIRO_ARGB_B];
1768 g = pimage[CAIRO_ARGB_G];
1769 r = pimage[CAIRO_ARGB_R];
1771 CAIRO_UNPREMULTIPLY (a, r, g, b);
1773 // convert background to yuv
1774 COMP_Y (y, r, g, b);
1775 COMP_U (u, r, g, b);
1776 COMP_V (v, r, g, b);
1778 // preform text "OVER" background alpha compositing
1779 a1 = a + (dest_ptr[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1780 OVER (dest_ptr[1], a, y, dest_ptr[0], dest_ptr[1], a1);
1781 OVER (dest_ptr[2], a, u, dest_ptr[0], dest_ptr[2], a1);
1782 OVER (dest_ptr[3], a, v, dest_ptr[0], dest_ptr[3], a1);
1783 dest_ptr[0] = a1 - 1; // remove the temporary 1 we added
1791 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1792 static inline void \
1793 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1794 GstVideoFrame * dest, gint xpos, gint ypos) \
1799 guchar *pimage, *dest_ptr; \
1800 guint8 *rgb_pixels;\
1802 rgb_pixels = dest->data[0];\
1804 w = overlay->image_width; \
1805 h = overlay->image_height; \
1811 if (xpos + w > overlay->width) { \
1812 w = overlay->width - xpos; \
1815 if (ypos + h > overlay->height) { \
1816 h = overlay->height - ypos; \
1819 for (i = 0; i < h; i++) { \
1820 pimage = overlay->text_image + i * overlay->image_width * 4; \
1821 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1822 for (j = 0; j < w; j++) { \
1823 a = pimage[CAIRO_ARGB_A]; \
1824 b = pimage[CAIRO_ARGB_B]; \
1825 g = pimage[CAIRO_ARGB_G]; \
1826 r = pimage[CAIRO_ARGB_R]; \
1827 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1828 b = (b*a + dest_ptr[B] * (255-a)) / 255; \
1829 g = (g*a + dest_ptr[G] * (255-a)) / 255; \
1830 r = (r*a + dest_ptr[R] * (255-a)) / 255; \
1840 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1841 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1842 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1843 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1845 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1846 static inline void \
1847 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1848 GstVideoFrame * dest, gint xpos, gint ypos) \
1850 int a, r, g, b, a1; \
1853 guchar *pimage, *dest_ptr; \
1854 guint8 *rgb_pixels;\
1856 rgb_pixels = dest->data[0];\
1858 w = overlay->image_width; \
1859 h = overlay->image_height; \
1865 if (xpos + w > overlay->width) { \
1866 w = overlay->width - xpos; \
1869 if (ypos + h > overlay->height) { \
1870 h = overlay->height - ypos; \
1873 for (i = 0; i < h; i++) { \
1874 pimage = overlay->text_image + i * overlay->image_width * 4; \
1875 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1876 for (j = 0; j < w; j++) { \
1877 a = pimage[CAIRO_ARGB_A]; \
1878 b = pimage[CAIRO_ARGB_B]; \
1879 g = pimage[CAIRO_ARGB_G]; \
1880 r = pimage[CAIRO_ARGB_R]; \
1881 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1882 a1 = a + (dest_ptr[A] * (255 - a)) / 255 + 1; \
1883 OVER (dest_ptr[R], a, r, dest_ptr[0], dest_ptr[R], a1); \
1884 OVER (dest_ptr[G], a, g, dest_ptr[0], dest_ptr[G], a1); \
1885 OVER (dest_ptr[B], a, b, dest_ptr[0], dest_ptr[B], a1); \
1886 dest_ptr[A] = a1 - 1; \
1892 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1893 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1894 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1895 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1898 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1899 const gchar * text, gint textlen)
1903 if (!overlay->need_render) {
1904 GST_DEBUG ("Using previously rendered text.");
1908 /* -1 is the whole string */
1909 if (text != NULL && textlen < 0) {
1910 textlen = strlen (text);
1914 string = g_strndup (text, textlen);
1915 } else { /* empty string */
1916 string = g_strdup (" ");
1918 g_strdelimit (string, "\r\t", ' ');
1919 textlen = strlen (string);
1921 /* FIXME: should we check for UTF-8 here? */
1923 GST_DEBUG ("Rendering '%s'", string);
1924 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1928 overlay->need_render = FALSE;
1931 static GstFlowReturn
1932 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1933 GstBuffer * video_frame)
1937 GstBaseTextOverlayVAlign valign;
1938 GstBaseTextOverlayHAlign halign;
1939 GstVideoFrame frame;
1941 width = overlay->image_width;
1942 height = overlay->image_height;
1944 video_frame = gst_buffer_make_writable (video_frame);
1946 if (!gst_video_frame_map (&frame, &overlay->info, video_frame, GST_MAP_WRITE))
1949 if (overlay->use_vertical_render)
1950 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1952 halign = overlay->halign;
1955 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1956 xpos = overlay->xpad;
1958 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1959 xpos = (overlay->width - width) / 2;
1961 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1962 xpos = overlay->width - width - overlay->xpad;
1964 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1965 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1966 xpos = CLAMP (xpos, 0, overlay->width - width);
1973 xpos += overlay->deltax;
1975 if (overlay->use_vertical_render)
1976 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1978 valign = overlay->valign;
1981 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1982 ypos = overlay->height - height - overlay->ypad;
1984 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1985 ypos = overlay->height - (height + overlay->ypad);
1987 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1988 ypos = overlay->ypad;
1990 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1991 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1992 ypos = CLAMP (ypos, 0, overlay->height - height);
1994 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1995 ypos = (overlay->height - height) / 2;
1998 ypos = overlay->ypad;
2001 ypos += overlay->deltay;
2003 /* shaded background box */
2004 if (overlay->want_shading) {
2005 switch (overlay->format) {
2006 case GST_VIDEO_FORMAT_I420:
2007 case GST_VIDEO_FORMAT_NV12:
2008 case GST_VIDEO_FORMAT_NV21:
2009 gst_base_text_overlay_shade_planar_Y (overlay, &frame,
2010 xpos, xpos + overlay->image_width,
2011 ypos, ypos + overlay->image_height);
2013 case GST_VIDEO_FORMAT_AYUV:
2014 case GST_VIDEO_FORMAT_UYVY:
2015 gst_base_text_overlay_shade_packed_Y (overlay, &frame,
2016 xpos, xpos + overlay->image_width,
2017 ypos, ypos + overlay->image_height);
2019 case GST_VIDEO_FORMAT_xRGB:
2020 gst_base_text_overlay_shade_xRGB (overlay, &frame,
2021 xpos, xpos + overlay->image_width,
2022 ypos, ypos + overlay->image_height);
2024 case GST_VIDEO_FORMAT_xBGR:
2025 gst_base_text_overlay_shade_xBGR (overlay, &frame,
2026 xpos, xpos + overlay->image_width,
2027 ypos, ypos + overlay->image_height);
2029 case GST_VIDEO_FORMAT_BGRx:
2030 gst_base_text_overlay_shade_BGRx (overlay, &frame,
2031 xpos, xpos + overlay->image_width,
2032 ypos, ypos + overlay->image_height);
2034 case GST_VIDEO_FORMAT_RGBx:
2035 gst_base_text_overlay_shade_RGBx (overlay, &frame,
2036 xpos, xpos + overlay->image_width,
2037 ypos, ypos + overlay->image_height);
2039 case GST_VIDEO_FORMAT_ARGB:
2040 gst_base_text_overlay_shade_ARGB (overlay, &frame,
2041 xpos, xpos + overlay->image_width,
2042 ypos, ypos + overlay->image_height);
2044 case GST_VIDEO_FORMAT_ABGR:
2045 gst_base_text_overlay_shade_ABGR (overlay, &frame,
2046 xpos, xpos + overlay->image_width,
2047 ypos, ypos + overlay->image_height);
2049 case GST_VIDEO_FORMAT_RGBA:
2050 gst_base_text_overlay_shade_RGBA (overlay, &frame,
2051 xpos, xpos + overlay->image_width,
2052 ypos, ypos + overlay->image_height);
2054 case GST_VIDEO_FORMAT_BGRA:
2055 gst_base_text_overlay_shade_BGRA (overlay, &frame,
2056 xpos, xpos + overlay->image_width,
2057 ypos, ypos + overlay->image_height);
2060 g_assert_not_reached ();
2067 if (overlay->text_image) {
2068 switch (overlay->format) {
2069 case GST_VIDEO_FORMAT_I420:
2070 gst_base_text_overlay_blit_I420 (overlay, &frame, xpos, ypos);
2072 case GST_VIDEO_FORMAT_NV12:
2073 case GST_VIDEO_FORMAT_NV21:
2074 gst_base_text_overlay_blit_NV12_NV21 (overlay, &frame, xpos, ypos);
2076 case GST_VIDEO_FORMAT_UYVY:
2077 gst_base_text_overlay_blit_UYVY (overlay, &frame, xpos, ypos);
2079 case GST_VIDEO_FORMAT_AYUV:
2080 gst_base_text_overlay_blit_AYUV (overlay, &frame, xpos, ypos);
2082 case GST_VIDEO_FORMAT_BGRx:
2083 gst_base_text_overlay_blit_BGRx (overlay, &frame, xpos, ypos);
2085 case GST_VIDEO_FORMAT_xRGB:
2086 gst_base_text_overlay_blit_xRGB (overlay, &frame, xpos, ypos);
2088 case GST_VIDEO_FORMAT_RGBx:
2089 gst_base_text_overlay_blit_RGBx (overlay, &frame, xpos, ypos);
2091 case GST_VIDEO_FORMAT_xBGR:
2092 gst_base_text_overlay_blit_xBGR (overlay, &frame, xpos, ypos);
2094 case GST_VIDEO_FORMAT_ARGB:
2095 gst_base_text_overlay_blit_ARGB (overlay, &frame, xpos, ypos);
2097 case GST_VIDEO_FORMAT_ABGR:
2098 gst_base_text_overlay_blit_ABGR (overlay, &frame, xpos, ypos);
2100 case GST_VIDEO_FORMAT_RGBA:
2101 gst_base_text_overlay_blit_RGBA (overlay, &frame, xpos, ypos);
2103 case GST_VIDEO_FORMAT_BGRA:
2104 gst_base_text_overlay_blit_BGRA (overlay, &frame, xpos, ypos);
2107 g_assert_not_reached ();
2110 gst_video_frame_unmap (&frame);
2112 return gst_pad_push (overlay->srcpad, video_frame);
2117 GST_DEBUG_OBJECT (overlay, "received invalid buffer");
2122 static GstPadLinkReturn
2123 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2125 GstBaseTextOverlay *overlay;
2127 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2128 if (G_UNLIKELY (!overlay))
2129 return GST_PAD_LINK_REFUSED;
2131 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2133 overlay->text_linked = TRUE;
2135 gst_object_unref (overlay);
2137 return GST_PAD_LINK_OK;
2141 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2143 GstBaseTextOverlay *overlay;
2145 /* don't use gst_pad_get_parent() here, will deadlock */
2146 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2148 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2150 overlay->text_linked = FALSE;
2152 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2156 gst_base_text_overlay_text_event (GstPad * pad, GstEvent * event)
2158 gboolean ret = FALSE;
2159 GstBaseTextOverlay *overlay = NULL;
2161 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2162 if (G_UNLIKELY (!overlay)) {
2163 gst_event_unref (event);
2167 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2169 switch (GST_EVENT_TYPE (event)) {
2170 case GST_EVENT_CAPS:
2174 gst_event_parse_caps (event, &caps);
2175 ret = gst_base_text_overlay_setcaps_txt (overlay, caps);
2176 gst_event_unref (event);
2179 case GST_EVENT_SEGMENT:
2181 const GstSegment *segment;
2183 overlay->text_eos = FALSE;
2185 gst_event_parse_segment (event, &segment);
2187 if (segment->format == GST_FORMAT_TIME) {
2188 GST_OBJECT_LOCK (overlay);
2189 gst_segment_copy_into (segment, &overlay->text_segment);
2190 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2191 &overlay->text_segment);
2192 GST_OBJECT_UNLOCK (overlay);
2194 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2195 ("received non-TIME newsegment event on text input"));
2198 gst_event_unref (event);
2201 /* wake up the video chain, it might be waiting for a text buffer or
2202 * a text segment update */
2203 GST_OBJECT_LOCK (overlay);
2204 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2205 GST_OBJECT_UNLOCK (overlay);
2208 case GST_EVENT_FLUSH_STOP:
2209 GST_OBJECT_LOCK (overlay);
2210 GST_INFO_OBJECT (overlay, "text flush stop");
2211 overlay->text_flushing = FALSE;
2212 overlay->text_eos = FALSE;
2213 gst_base_text_overlay_pop_text (overlay);
2214 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2215 GST_OBJECT_UNLOCK (overlay);
2216 gst_event_unref (event);
2219 case GST_EVENT_FLUSH_START:
2220 GST_OBJECT_LOCK (overlay);
2221 GST_INFO_OBJECT (overlay, "text flush start");
2222 overlay->text_flushing = TRUE;
2223 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2224 GST_OBJECT_UNLOCK (overlay);
2225 gst_event_unref (event);
2229 GST_OBJECT_LOCK (overlay);
2230 overlay->text_eos = TRUE;
2231 GST_INFO_OBJECT (overlay, "text EOS");
2232 /* wake up the video chain, it might be waiting for a text buffer or
2233 * a text segment update */
2234 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2235 GST_OBJECT_UNLOCK (overlay);
2236 gst_event_unref (event);
2240 ret = gst_pad_event_default (pad, event);
2244 gst_object_unref (overlay);
2250 gst_base_text_overlay_video_event (GstPad * pad, GstEvent * event)
2252 gboolean ret = FALSE;
2253 GstBaseTextOverlay *overlay = NULL;
2255 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2256 if (G_UNLIKELY (!overlay)) {
2257 gst_event_unref (event);
2261 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2263 switch (GST_EVENT_TYPE (event)) {
2264 case GST_EVENT_CAPS:
2268 gst_event_parse_caps (event, &caps);
2269 ret = gst_base_text_overlay_setcaps (overlay, caps);
2270 gst_event_unref (event);
2273 case GST_EVENT_SEGMENT:
2275 const GstSegment *segment;
2277 GST_DEBUG_OBJECT (overlay, "received new segment");
2279 gst_event_parse_segment (event, &segment);
2281 if (segment->format == GST_FORMAT_TIME) {
2282 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2285 gst_segment_copy_into (segment, &overlay->segment);
2287 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2288 ("received non-TIME newsegment event on video input"));
2291 ret = gst_pad_event_default (pad, event);
2295 GST_OBJECT_LOCK (overlay);
2296 GST_INFO_OBJECT (overlay, "video EOS");
2297 overlay->video_eos = TRUE;
2298 GST_OBJECT_UNLOCK (overlay);
2299 ret = gst_pad_event_default (pad, event);
2301 case GST_EVENT_FLUSH_START:
2302 GST_OBJECT_LOCK (overlay);
2303 GST_INFO_OBJECT (overlay, "video flush start");
2304 overlay->video_flushing = TRUE;
2305 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2306 GST_OBJECT_UNLOCK (overlay);
2307 ret = gst_pad_event_default (pad, event);
2309 case GST_EVENT_FLUSH_STOP:
2310 GST_OBJECT_LOCK (overlay);
2311 GST_INFO_OBJECT (overlay, "video flush stop");
2312 overlay->video_flushing = FALSE;
2313 overlay->video_eos = FALSE;
2314 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2315 GST_OBJECT_UNLOCK (overlay);
2316 ret = gst_pad_event_default (pad, event);
2319 ret = gst_pad_event_default (pad, event);
2323 gst_object_unref (overlay);
2329 gst_base_text_overlay_video_query (GstPad * pad, GstQuery * query)
2331 gboolean ret = FALSE;
2332 GstBaseTextOverlay *overlay = NULL;
2334 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2335 if (G_UNLIKELY (!overlay)) {
2339 switch (GST_QUERY_TYPE (query)) {
2340 case GST_QUERY_CAPS:
2342 GstCaps *filter, *caps;
2344 gst_query_parse_caps (query, &filter);
2345 caps = gst_base_text_overlay_getcaps (pad, filter);
2346 gst_query_set_caps_result (query, caps);
2347 gst_caps_unref (caps);
2352 ret = gst_pad_query_default (pad, query);
2356 gst_object_unref (overlay);
2361 /* Called with lock held */
2363 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2365 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2367 if (overlay->text_buffer) {
2368 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2369 overlay->text_buffer);
2370 gst_buffer_unref (overlay->text_buffer);
2371 overlay->text_buffer = NULL;
2374 /* Let the text task know we used that buffer */
2375 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2378 /* We receive text buffers here. If they are out of segment we just ignore them.
2379 If the buffer is in our segment we keep it internally except if another one
2380 is already waiting here, in that case we wait that it gets kicked out */
2381 static GstFlowReturn
2382 gst_base_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
2384 GstFlowReturn ret = GST_FLOW_OK;
2385 GstBaseTextOverlay *overlay = NULL;
2386 gboolean in_seg = FALSE;
2387 guint64 clip_start = 0, clip_stop = 0;
2389 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2391 GST_OBJECT_LOCK (overlay);
2393 if (overlay->text_flushing) {
2394 GST_OBJECT_UNLOCK (overlay);
2395 ret = GST_FLOW_WRONG_STATE;
2396 GST_LOG_OBJECT (overlay, "text flushing");
2400 if (overlay->text_eos) {
2401 GST_OBJECT_UNLOCK (overlay);
2403 GST_LOG_OBJECT (overlay, "text EOS");
2407 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2408 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2409 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2410 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2411 GST_BUFFER_DURATION (buffer)));
2413 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2416 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2417 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2419 stop = GST_CLOCK_TIME_NONE;
2421 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2422 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2428 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2429 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2430 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2431 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2433 /* Wait for the previous buffer to go away */
2434 while (overlay->text_buffer != NULL) {
2435 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2436 GST_DEBUG_PAD_NAME (pad));
2437 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2438 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2439 if (overlay->text_flushing) {
2440 GST_OBJECT_UNLOCK (overlay);
2441 ret = GST_FLOW_WRONG_STATE;
2446 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2447 overlay->text_segment.position = clip_start;
2449 overlay->text_buffer = buffer;
2450 /* That's a new text buffer we need to render */
2451 overlay->need_render = TRUE;
2453 /* in case the video chain is waiting for a text buffer, wake it up */
2454 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2457 GST_OBJECT_UNLOCK (overlay);
2464 static GstFlowReturn
2465 gst_base_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
2467 GstBaseTextOverlayClass *klass;
2468 GstBaseTextOverlay *overlay;
2469 GstFlowReturn ret = GST_FLOW_OK;
2470 gboolean in_seg = FALSE;
2471 guint64 start, stop, clip_start = 0, clip_stop = 0;
2474 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2475 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2477 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2478 goto missing_timestamp;
2480 /* ignore buffers that are outside of the current segment */
2481 start = GST_BUFFER_TIMESTAMP (buffer);
2483 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2484 stop = GST_CLOCK_TIME_NONE;
2486 stop = start + GST_BUFFER_DURATION (buffer);
2489 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2490 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2491 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2493 /* segment_clip() will adjust start unconditionally to segment_start if
2494 * no stop time is provided, so handle this ourselves */
2495 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2496 goto out_of_segment;
2498 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2499 &clip_start, &clip_stop);
2502 goto out_of_segment;
2504 /* if the buffer is only partially in the segment, fix up stamps */
2505 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2506 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2507 buffer = gst_buffer_make_writable (buffer);
2508 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2510 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2513 /* now, after we've done the clipping, fix up end time if there's no
2514 * duration (we only use those estimated values internally though, we
2515 * don't want to set bogus values on the buffer itself) */
2519 gint fps_num, fps_denom;
2521 /* FIXME, store this in setcaps */
2522 caps = gst_pad_get_current_caps (pad);
2523 s = gst_caps_get_structure (caps, 0);
2524 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2525 fps_num && fps_denom) {
2526 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2527 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2529 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2530 stop = start + 1; /* we need to assume some interval */
2532 gst_caps_unref (caps);
2535 gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2539 GST_OBJECT_LOCK (overlay);
2541 if (overlay->video_flushing)
2544 if (overlay->video_eos)
2547 if (overlay->silent) {
2548 GST_OBJECT_UNLOCK (overlay);
2549 ret = gst_pad_push (overlay->srcpad, buffer);
2551 /* Update position */
2552 overlay->segment.position = clip_start;
2557 /* Text pad not linked, rendering internal text */
2558 if (!overlay->text_linked) {
2559 if (klass->get_text) {
2560 text = klass->get_text (overlay, buffer);
2562 text = g_strdup (overlay->default_text);
2565 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2566 "text: '%s'", GST_STR_NULL (text));
2568 GST_OBJECT_UNLOCK (overlay);
2570 if (text != NULL && *text != '\0') {
2571 /* Render and push */
2572 gst_base_text_overlay_render_text (overlay, text, -1);
2573 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2575 /* Invalid or empty string */
2576 ret = gst_pad_push (overlay->srcpad, buffer);
2579 /* Text pad linked, check if we have a text buffer queued */
2580 if (overlay->text_buffer) {
2581 gboolean pop_text = FALSE, valid_text_time = TRUE;
2582 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2583 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2584 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2585 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2586 GstClockTime vid_running_time, vid_running_time_end;
2588 /* if the text buffer isn't stamped right, pop it off the
2589 * queue and display it for the current video frame only */
2590 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2591 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2592 GST_WARNING_OBJECT (overlay,
2593 "Got text buffer with invalid timestamp or duration");
2595 valid_text_time = FALSE;
2597 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2598 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2602 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2604 vid_running_time_end =
2605 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2608 /* If timestamp and duration are valid */
2609 if (valid_text_time) {
2611 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2613 text_running_time_end =
2614 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2618 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2619 GST_TIME_ARGS (text_running_time),
2620 GST_TIME_ARGS (text_running_time_end));
2621 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2622 GST_TIME_ARGS (vid_running_time),
2623 GST_TIME_ARGS (vid_running_time_end));
2625 /* Text too old or in the future */
2626 if (valid_text_time && text_running_time_end <= vid_running_time) {
2627 /* text buffer too old, get rid of it and do nothing */
2628 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2630 gst_base_text_overlay_pop_text (overlay);
2631 GST_OBJECT_UNLOCK (overlay);
2632 goto wait_for_text_buf;
2633 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2634 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2635 GST_OBJECT_UNLOCK (overlay);
2636 /* Push the video frame */
2637 ret = gst_pad_push (overlay->srcpad, buffer);
2639 gchar *in_text, *otext;
2640 gsize in_size, osize;
2643 gst_buffer_map (overlay->text_buffer, &osize, NULL, GST_MAP_READ);
2647 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2648 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2649 * here on purpose, this is something that needs fixing upstream */
2650 if (!g_utf8_validate (in_text, in_size, NULL)) {
2651 const gchar *end = NULL;
2653 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2654 in_text = g_strndup (in_text, in_size);
2655 while (!g_utf8_validate (in_text, in_size, &end) && end)
2656 *((gchar *) end) = '*';
2659 /* Get the string */
2660 if (overlay->have_pango_markup) {
2661 text = g_strndup (in_text, in_size);
2663 text = g_markup_escape_text (in_text, in_size);
2666 if (text != NULL && *text != '\0') {
2667 gint text_len = strlen (text);
2669 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2670 text[text_len - 1] == '\r')) {
2673 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2674 gst_base_text_overlay_render_text (overlay, text, text_len);
2676 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2677 gst_base_text_overlay_render_text (overlay, " ", 1);
2679 gst_buffer_unmap (overlay->text_buffer, otext, osize);
2681 if (in_text != otext)
2684 GST_OBJECT_UNLOCK (overlay);
2685 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2687 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2688 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2693 GST_OBJECT_LOCK (overlay);
2694 gst_base_text_overlay_pop_text (overlay);
2695 GST_OBJECT_UNLOCK (overlay);
2698 gboolean wait_for_text_buf = TRUE;
2700 if (overlay->text_eos)
2701 wait_for_text_buf = FALSE;
2703 if (!overlay->wait_text)
2704 wait_for_text_buf = FALSE;
2706 /* Text pad linked, but no text buffer available - what now? */
2707 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2708 GstClockTime text_start_running_time, text_position_running_time;
2709 GstClockTime vid_running_time;
2712 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2713 GST_BUFFER_TIMESTAMP (buffer));
2714 text_start_running_time =
2715 gst_segment_to_running_time (&overlay->text_segment,
2716 GST_FORMAT_TIME, overlay->text_segment.start);
2717 text_position_running_time =
2718 gst_segment_to_running_time (&overlay->text_segment,
2719 GST_FORMAT_TIME, overlay->text_segment.position);
2721 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2722 vid_running_time < text_start_running_time) ||
2723 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2724 vid_running_time < text_position_running_time)) {
2725 wait_for_text_buf = FALSE;
2729 if (wait_for_text_buf) {
2730 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2731 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2732 GST_DEBUG_OBJECT (overlay, "resuming");
2733 GST_OBJECT_UNLOCK (overlay);
2734 goto wait_for_text_buf;
2736 GST_OBJECT_UNLOCK (overlay);
2737 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2738 ret = gst_pad_push (overlay->srcpad, buffer);
2745 /* Update position */
2746 overlay->segment.position = clip_start;
2752 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2753 gst_buffer_unref (buffer);
2759 GST_OBJECT_UNLOCK (overlay);
2760 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2761 gst_buffer_unref (buffer);
2762 return GST_FLOW_WRONG_STATE;
2766 GST_OBJECT_UNLOCK (overlay);
2767 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2768 gst_buffer_unref (buffer);
2769 return GST_FLOW_EOS;
2773 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2774 gst_buffer_unref (buffer);
2779 static GstStateChangeReturn
2780 gst_base_text_overlay_change_state (GstElement * element,
2781 GstStateChange transition)
2783 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2784 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2786 switch (transition) {
2787 case GST_STATE_CHANGE_PAUSED_TO_READY:
2788 GST_OBJECT_LOCK (overlay);
2789 overlay->text_flushing = TRUE;
2790 overlay->video_flushing = TRUE;
2791 /* pop_text will broadcast on the GCond and thus also make the video
2792 * chain exit if it's waiting for a text buffer */
2793 gst_base_text_overlay_pop_text (overlay);
2794 GST_OBJECT_UNLOCK (overlay);
2800 ret = parent_class->change_state (element, transition);
2801 if (ret == GST_STATE_CHANGE_FAILURE)
2804 switch (transition) {
2805 case GST_STATE_CHANGE_READY_TO_PAUSED:
2806 GST_OBJECT_LOCK (overlay);
2807 overlay->text_flushing = FALSE;
2808 overlay->video_flushing = FALSE;
2809 overlay->video_eos = FALSE;
2810 overlay->text_eos = FALSE;
2811 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2812 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2813 GST_OBJECT_UNLOCK (overlay);
2823 plugin_init (GstPlugin * plugin)
2825 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2826 GST_TYPE_TEXT_OVERLAY) ||
2827 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2828 GST_TYPE_TIME_OVERLAY) ||
2829 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2830 GST_TYPE_CLOCK_OVERLAY) ||
2831 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2832 GST_TYPE_TEXT_RENDER)) {
2836 /*texttestsrc_plugin_init(module, plugin); */
2838 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2843 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2844 "pango", "Pango-based text rendering and overlay", plugin_init,
2845 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)