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,
318 GstObject * parent, GstQuery * query);
320 static gboolean gst_base_text_overlay_video_event (GstPad * pad,
322 static gboolean gst_base_text_overlay_video_query (GstPad * pad,
323 GstObject * parent, GstQuery * query);
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, GstObject * parent,
999 gboolean ret = FALSE;
1000 GstBaseTextOverlay *overlay = NULL;
1002 overlay = GST_BASE_TEXT_OVERLAY (parent);
1004 switch (GST_QUERY_TYPE (query)) {
1005 case GST_QUERY_CAPS:
1007 GstCaps *filter, *caps;
1009 gst_query_parse_caps (query, &filter);
1010 caps = gst_base_text_overlay_getcaps (pad, filter);
1011 gst_query_set_caps_result (query, caps);
1012 gst_caps_unref (caps);
1017 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1025 gst_base_text_overlay_src_event (GstPad * pad, GstEvent * event)
1027 gboolean ret = FALSE;
1028 GstBaseTextOverlay *overlay = NULL;
1030 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1031 if (G_UNLIKELY (!overlay)) {
1032 gst_event_unref (event);
1036 switch (GST_EVENT_TYPE (event)) {
1037 case GST_EVENT_SEEK:{
1040 /* We don't handle seek if we have not text pad */
1041 if (!overlay->text_linked) {
1042 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1043 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1047 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1049 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1051 /* Flush downstream, only for flushing seek */
1052 if (flags & GST_SEEK_FLAG_FLUSH)
1053 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1055 /* Mark ourself as flushing, unblock chains */
1056 GST_OBJECT_LOCK (overlay);
1057 overlay->video_flushing = TRUE;
1058 overlay->text_flushing = TRUE;
1059 gst_base_text_overlay_pop_text (overlay);
1060 GST_OBJECT_UNLOCK (overlay);
1062 /* Seek on each sink pad */
1063 gst_event_ref (event);
1064 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1066 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1068 gst_event_unref (event);
1073 if (overlay->text_linked) {
1074 gst_event_ref (event);
1075 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1076 gst_pad_push_event (overlay->text_sinkpad, event);
1078 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1084 gst_object_unref (overlay);
1090 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1092 GstBaseTextOverlay *overlay;
1096 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1097 if (G_UNLIKELY (!overlay))
1098 return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1100 if (pad == overlay->srcpad)
1101 otherpad = overlay->video_sinkpad;
1103 otherpad = overlay->srcpad;
1105 /* we can do what the peer can */
1106 caps = gst_pad_peer_query_caps (otherpad, filter);
1108 GstCaps *temp, *templ;
1110 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1112 /* filtered against our padtemplate */
1113 templ = gst_pad_get_pad_template_caps (otherpad);
1114 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1115 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1116 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1117 gst_caps_unref (caps);
1118 gst_caps_unref (templ);
1119 /* this is what we can do */
1122 /* no peer, our padtemplate is enough then */
1123 caps = gst_pad_get_pad_template_caps (pad);
1125 GstCaps *intersection;
1128 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1129 gst_caps_unref (caps);
1130 caps = intersection;
1134 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1136 gst_object_unref (overlay);
1142 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1143 PangoFontDescription * desc)
1145 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1146 overlay->shadow_offset = (double) (font_size) / 13.0;
1147 overlay->outline_offset = (double) (font_size) / 15.0;
1148 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1149 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1152 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1153 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1154 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1155 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1159 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1160 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1167 gint width = overlay->image_width;
1168 gint height = overlay->image_height;
1174 if (xpos + width > overlay->width) {
1175 width = overlay->width - xpos;
1178 if (ypos + height > overlay->height) {
1179 height = overlay->height - ypos;
1182 dest += (ypos / 1) * dest_stride;
1184 for (i = 0; i < height; i++) {
1185 pimage = text_image + 4 * (i * overlay->image_width);
1186 py = dest + i * dest_stride + xpos;
1187 for (j = 0; j < width; j++) {
1188 b = pimage[CAIRO_ARGB_B];
1189 g = pimage[CAIRO_ARGB_G];
1190 r = pimage[CAIRO_ARGB_R];
1191 a = pimage[CAIRO_ARGB_A];
1192 CAIRO_UNPREMULTIPLY (a, r, g, b);
1199 COMP_Y (y, r, g, b);
1201 BLEND (*py++, a, y, x);
1207 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1208 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1209 guint destcb_stride, guint destcr_stride, guint pix_stride)
1214 gushort r1, g1, b1, a1;
1215 guchar *pimage1, *pimage2;
1217 gint width = overlay->image_width - 2;
1218 gint height = overlay->image_height - 2;
1226 if (xpos + width > overlay->width) {
1227 width = overlay->width - xpos;
1230 if (ypos + height > overlay->height) {
1231 height = overlay->height - ypos;
1234 destcb += (ypos / 2) * destcb_stride;
1235 destcr += (ypos / 2) * destcr_stride;
1237 for (i = 0; i < height; i += 2) {
1238 pimage1 = text_image + 4 * (i * overlay->image_width);
1239 pimage2 = pimage1 + 4 * overlay->image_width;
1240 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1241 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1242 for (j = 0; j < width; j += 2) {
1243 b = pimage1[CAIRO_ARGB_B];
1244 g = pimage1[CAIRO_ARGB_G];
1245 r = pimage1[CAIRO_ARGB_R];
1246 a = pimage1[CAIRO_ARGB_A];
1247 CAIRO_UNPREMULTIPLY (a, r, g, b);
1250 b1 = pimage1[CAIRO_ARGB_B];
1251 g1 = pimage1[CAIRO_ARGB_G];
1252 r1 = pimage1[CAIRO_ARGB_R];
1253 a1 = pimage1[CAIRO_ARGB_A];
1254 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
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);
1272 /* + 2 for rounding */
1273 b1 = pimage2[CAIRO_ARGB_B];
1274 g1 = pimage2[CAIRO_ARGB_G];
1275 r1 = pimage2[CAIRO_ARGB_R];
1276 a1 = pimage2[CAIRO_ARGB_A];
1277 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1294 COMP_U (cb, r, g, b);
1295 COMP_V (cr, r, g, b);
1298 BLEND (*pcb, a, cb, x);
1300 BLEND (*pcr, a, cr, x);
1309 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1310 const gchar * string, gint textlen)
1313 cairo_surface_t *surface;
1314 PangoRectangle ink_rect, logical_rect;
1315 cairo_matrix_t cairo_matrix;
1317 double scalef = 1.0;
1320 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1322 if (overlay->auto_adjust_size) {
1323 /* 640 pixel is default */
1324 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1326 pango_layout_set_width (overlay->layout, -1);
1327 /* set text on pango layout */
1328 pango_layout_set_markup (overlay->layout, string, textlen);
1330 /* get subtitle image size */
1331 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1333 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1335 if (width + overlay->deltax >
1336 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1338 * subtitle image width is larger then overlay width
1339 * so rearrange overlay wrap mode.
1341 gst_base_text_overlay_update_wrap_mode (overlay);
1342 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1343 width = overlay->width;
1347 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1348 if (height > overlay->height) {
1349 height = overlay->height;
1351 if (overlay->use_vertical_render) {
1352 PangoRectangle rect;
1353 PangoContext *context;
1354 PangoMatrix matrix = PANGO_MATRIX_INIT;
1357 context = pango_layout_get_context (overlay->layout);
1359 pango_matrix_rotate (&matrix, -90);
1361 rect.x = rect.y = 0;
1363 rect.height = height;
1364 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1365 matrix.x0 = -rect.x;
1366 matrix.y0 = -rect.y;
1368 pango_context_set_matrix (context, &matrix);
1370 cairo_matrix.xx = matrix.xx;
1371 cairo_matrix.yx = matrix.yx;
1372 cairo_matrix.xy = matrix.xy;
1373 cairo_matrix.yy = matrix.yy;
1374 cairo_matrix.x0 = matrix.x0;
1375 cairo_matrix.y0 = matrix.y0;
1376 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1382 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1385 /* reallocate surface */
1386 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1388 surface = cairo_image_surface_create_for_data (overlay->text_image,
1389 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1390 cr = cairo_create (surface);
1393 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1396 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1398 if (overlay->want_shading)
1399 cairo_paint_with_alpha (cr, overlay->shading_value);
1401 /* apply transformations */
1402 cairo_set_matrix (cr, &cairo_matrix);
1404 /* FIXME: We use show_layout everywhere except for the surface
1405 * because it's really faster and internally does all kinds of
1406 * caching. Unfortunately we have to paint to a cairo path for
1407 * the outline and this is slow. Once Pango supports user fonts
1408 * we should use them, see
1409 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1411 * Idea would the be, to create a cairo user font that
1412 * does shadow, outline, text painting in the
1413 * render_glyph function.
1416 /* draw shadow text */
1418 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1419 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1420 pango_cairo_show_layout (cr, overlay->layout);
1423 a = (overlay->outline_color >> 24) & 0xff;
1424 r = (overlay->outline_color >> 16) & 0xff;
1425 g = (overlay->outline_color >> 8) & 0xff;
1426 b = (overlay->outline_color >> 0) & 0xff;
1428 /* draw outline text */
1430 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1431 cairo_set_line_width (cr, overlay->outline_offset);
1432 pango_cairo_layout_path (cr, overlay->layout);
1436 a = (overlay->color >> 24) & 0xff;
1437 r = (overlay->color >> 16) & 0xff;
1438 g = (overlay->color >> 8) & 0xff;
1439 b = (overlay->color >> 0) & 0xff;
1443 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1444 pango_cairo_show_layout (cr, overlay->layout);
1448 cairo_surface_destroy (surface);
1449 overlay->image_width = width;
1450 overlay->image_height = height;
1451 overlay->baseline_y = ink_rect.y;
1452 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1459 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1460 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1462 gint i, j, dest_stride;
1465 dest_stride = dest->info.stride[0];
1466 dest_ptr = dest->data[0];
1468 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1469 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1471 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1472 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1474 for (i = y0; i < y1; ++i) {
1475 for (j = x0; j < x1; ++j) {
1476 gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
1478 dest_ptr[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1484 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1485 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1488 guint dest_stride, pixel_stride;
1491 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
1492 dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
1493 pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
1495 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1496 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1498 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1499 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1502 x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
1504 x1 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x1);
1507 y0 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y0);
1509 y1 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y1);
1511 for (i = y0; i < y1; i++) {
1512 for (j = x0; j < x1; j++) {
1516 y_pos = (i * dest_stride) + j * pixel_stride;
1517 y = dest_ptr[y_pos] + overlay->shading_value;
1519 dest_ptr[y_pos] = CLAMP (y, 0, 255);
1524 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1525 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1526 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1528 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
1529 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1534 dest_ptr = dest->data[0];
1536 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1537 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1539 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1540 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1542 for (i = y0; i < y1; i++) {
1543 for (j = x0; j < x1; j++) {
1546 y_pos = (i * 4 * overlay->width) + j * 4;
1547 for (k = 0; k < 4; k++) {
1548 y = dest_ptr[y_pos + k] + overlay->shading_value;
1549 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);
1555 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1556 static inline void \
1557 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
1558 gint x0, gint x1, gint y0, gint y1) \
1563 dest_ptr = dest->data[0];\
1565 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1566 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1568 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1569 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1571 for (i = y0; i < y1; i++) {\
1572 for (j = x0; j < x1; j++) {\
1574 y_pos = (i * 4 * overlay->width) + j * 4;\
1575 for (k = OFFSET; k < 3+OFFSET; k++) {\
1576 y = dest_ptr[y_pos + k] + overlay->shading_value;\
1577 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);\
1582 ARGB_SHADE_FUNCTION (ARGB, 1);
1583 ARGB_SHADE_FUNCTION (ABGR, 1);
1584 ARGB_SHADE_FUNCTION (RGBA, 0);
1585 ARGB_SHADE_FUNCTION (BGRA, 0);
1589 * - use proper strides and offset for I420
1590 * - don't draw over the edge of the picture (try a longer
1591 * text with a huge font size)
1595 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1596 GstVideoFrame * dest, gint xpos, gint ypos)
1598 int y_stride, u_stride, v_stride;
1599 guint8 *y_pixels, *u_pixels, *v_pixels;
1601 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1602 * to a boundary of integer number of U/V pixels:
1604 xpos = GST_ROUND_UP_2 (xpos);
1605 ypos = GST_ROUND_UP_2 (ypos);
1607 y_pixels = dest->data[0];
1608 u_pixels = dest->data[1];
1609 v_pixels = dest->data[2];
1610 y_stride = dest->info.stride[0];
1611 u_stride = dest->info.stride[1];
1612 v_stride = dest->info.stride[2];
1614 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1615 overlay->text_image, y_stride);
1616 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1617 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 2);
1621 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1622 GstVideoFrame * dest, gint xpos, gint ypos)
1624 int y_stride, u_stride, v_stride;
1625 guint8 *y_pixels, *u_pixels, *v_pixels;
1627 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1628 * to a boundary of integer number of U/V pixels:
1630 xpos = GST_ROUND_UP_2 (xpos);
1631 ypos = GST_ROUND_UP_2 (ypos);
1633 y_pixels = dest->data[0];
1634 u_pixels = dest->data[1];
1635 v_pixels = dest->data[2];
1636 y_stride = dest->info.stride[0];
1637 u_stride = dest->info.stride[1];
1638 v_stride = dest->info.stride[2];
1640 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1641 overlay->text_image, y_stride);
1642 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1643 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 1);
1647 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1648 GstVideoFrame * dest, gint xpos, gint ypos)
1655 guchar *pimage, *dest_ptr;
1658 yuv_pixels = dest->data[0];
1660 /* because U/V is 2x horizontally subsampled, we need to round to a
1661 * boundary of integer number of U/V pixels in x dimension:
1663 xpos = GST_ROUND_UP_2 (xpos);
1665 w = overlay->image_width - 2;
1666 h = overlay->image_height - 2;
1672 if (xpos + w > overlay->width) {
1673 w = overlay->width - xpos;
1676 if (ypos + h > overlay->height) {
1677 h = overlay->height - ypos;
1680 for (i = 0; i < h; i++) {
1681 pimage = overlay->text_image + i * overlay->image_width * 4;
1682 dest_ptr = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1683 for (j = 0; j < w; j += 2) {
1684 b0 = pimage[CAIRO_ARGB_B];
1685 g0 = pimage[CAIRO_ARGB_G];
1686 r0 = pimage[CAIRO_ARGB_R];
1687 a0 = pimage[CAIRO_ARGB_A];
1688 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1691 b1 = pimage[CAIRO_ARGB_B];
1692 g1 = pimage[CAIRO_ARGB_G];
1693 r1 = pimage[CAIRO_ARGB_R];
1694 a1 = pimage[CAIRO_ARGB_A];
1695 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1705 COMP_Y (y0, r0, g0, b0);
1706 COMP_Y (y1, r1, g1, b1);
1716 COMP_U (u, r0, g0, b0);
1717 COMP_V (v, r0, g0, b0);
1719 BLEND (*dest_ptr, a0, u, *dest_ptr);
1721 BLEND (*dest_ptr, a0, y0, *dest_ptr);
1723 BLEND (*dest_ptr, a0, v, *dest_ptr);
1725 BLEND (*dest_ptr, a0, y1, *dest_ptr);
1732 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1733 GstVideoFrame * dest, gint xpos, gint ypos)
1739 guchar *pimage, *dest_ptr;
1742 rgb_pixels = dest->data[0];
1744 w = overlay->image_width;
1745 h = overlay->image_height;
1751 if (xpos + w > overlay->width) {
1752 w = overlay->width - xpos;
1755 if (ypos + h > overlay->height) {
1756 h = overlay->height - ypos;
1759 for (i = 0; i < h; i++) {
1760 pimage = overlay->text_image + i * overlay->image_width * 4;
1761 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1762 for (j = 0; j < w; j++) {
1763 a = pimage[CAIRO_ARGB_A];
1764 b = pimage[CAIRO_ARGB_B];
1765 g = pimage[CAIRO_ARGB_G];
1766 r = pimage[CAIRO_ARGB_R];
1768 CAIRO_UNPREMULTIPLY (a, r, g, b);
1770 // convert background to yuv
1771 COMP_Y (y, r, g, b);
1772 COMP_U (u, r, g, b);
1773 COMP_V (v, r, g, b);
1775 // preform text "OVER" background alpha compositing
1776 a1 = a + (dest_ptr[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1777 OVER (dest_ptr[1], a, y, dest_ptr[0], dest_ptr[1], a1);
1778 OVER (dest_ptr[2], a, u, dest_ptr[0], dest_ptr[2], a1);
1779 OVER (dest_ptr[3], a, v, dest_ptr[0], dest_ptr[3], a1);
1780 dest_ptr[0] = a1 - 1; // remove the temporary 1 we added
1788 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1789 static inline void \
1790 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1791 GstVideoFrame * dest, gint xpos, gint ypos) \
1796 guchar *pimage, *dest_ptr; \
1797 guint8 *rgb_pixels;\
1799 rgb_pixels = dest->data[0];\
1801 w = overlay->image_width; \
1802 h = overlay->image_height; \
1808 if (xpos + w > overlay->width) { \
1809 w = overlay->width - xpos; \
1812 if (ypos + h > overlay->height) { \
1813 h = overlay->height - ypos; \
1816 for (i = 0; i < h; i++) { \
1817 pimage = overlay->text_image + i * overlay->image_width * 4; \
1818 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1819 for (j = 0; j < w; j++) { \
1820 a = pimage[CAIRO_ARGB_A]; \
1821 b = pimage[CAIRO_ARGB_B]; \
1822 g = pimage[CAIRO_ARGB_G]; \
1823 r = pimage[CAIRO_ARGB_R]; \
1824 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1825 b = (b*a + dest_ptr[B] * (255-a)) / 255; \
1826 g = (g*a + dest_ptr[G] * (255-a)) / 255; \
1827 r = (r*a + dest_ptr[R] * (255-a)) / 255; \
1837 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1838 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1839 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1840 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1842 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1843 static inline void \
1844 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1845 GstVideoFrame * dest, gint xpos, gint ypos) \
1847 int a, r, g, b, a1; \
1850 guchar *pimage, *dest_ptr; \
1851 guint8 *rgb_pixels;\
1853 rgb_pixels = dest->data[0];\
1855 w = overlay->image_width; \
1856 h = overlay->image_height; \
1862 if (xpos + w > overlay->width) { \
1863 w = overlay->width - xpos; \
1866 if (ypos + h > overlay->height) { \
1867 h = overlay->height - ypos; \
1870 for (i = 0; i < h; i++) { \
1871 pimage = overlay->text_image + i * overlay->image_width * 4; \
1872 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1873 for (j = 0; j < w; j++) { \
1874 a = pimage[CAIRO_ARGB_A]; \
1875 b = pimage[CAIRO_ARGB_B]; \
1876 g = pimage[CAIRO_ARGB_G]; \
1877 r = pimage[CAIRO_ARGB_R]; \
1878 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1879 a1 = a + (dest_ptr[A] * (255 - a)) / 255 + 1; \
1880 OVER (dest_ptr[R], a, r, dest_ptr[0], dest_ptr[R], a1); \
1881 OVER (dest_ptr[G], a, g, dest_ptr[0], dest_ptr[G], a1); \
1882 OVER (dest_ptr[B], a, b, dest_ptr[0], dest_ptr[B], a1); \
1883 dest_ptr[A] = a1 - 1; \
1889 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1890 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1891 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1892 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1895 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1896 const gchar * text, gint textlen)
1900 if (!overlay->need_render) {
1901 GST_DEBUG ("Using previously rendered text.");
1905 /* -1 is the whole string */
1906 if (text != NULL && textlen < 0) {
1907 textlen = strlen (text);
1911 string = g_strndup (text, textlen);
1912 } else { /* empty string */
1913 string = g_strdup (" ");
1915 g_strdelimit (string, "\r\t", ' ');
1916 textlen = strlen (string);
1918 /* FIXME: should we check for UTF-8 here? */
1920 GST_DEBUG ("Rendering '%s'", string);
1921 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1925 overlay->need_render = FALSE;
1928 static GstFlowReturn
1929 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1930 GstBuffer * video_frame)
1934 GstBaseTextOverlayVAlign valign;
1935 GstBaseTextOverlayHAlign halign;
1936 GstVideoFrame frame;
1938 width = overlay->image_width;
1939 height = overlay->image_height;
1941 video_frame = gst_buffer_make_writable (video_frame);
1943 if (!gst_video_frame_map (&frame, &overlay->info, video_frame, GST_MAP_WRITE))
1946 if (overlay->use_vertical_render)
1947 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1949 halign = overlay->halign;
1952 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1953 xpos = overlay->xpad;
1955 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1956 xpos = (overlay->width - width) / 2;
1958 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1959 xpos = overlay->width - width - overlay->xpad;
1961 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1962 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1963 xpos = CLAMP (xpos, 0, overlay->width - width);
1970 xpos += overlay->deltax;
1972 if (overlay->use_vertical_render)
1973 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1975 valign = overlay->valign;
1978 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1979 ypos = overlay->height - height - overlay->ypad;
1981 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1982 ypos = overlay->height - (height + overlay->ypad);
1984 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1985 ypos = overlay->ypad;
1987 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1988 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1989 ypos = CLAMP (ypos, 0, overlay->height - height);
1991 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1992 ypos = (overlay->height - height) / 2;
1995 ypos = overlay->ypad;
1998 ypos += overlay->deltay;
2000 /* shaded background box */
2001 if (overlay->want_shading) {
2002 switch (overlay->format) {
2003 case GST_VIDEO_FORMAT_I420:
2004 case GST_VIDEO_FORMAT_NV12:
2005 case GST_VIDEO_FORMAT_NV21:
2006 gst_base_text_overlay_shade_planar_Y (overlay, &frame,
2007 xpos, xpos + overlay->image_width,
2008 ypos, ypos + overlay->image_height);
2010 case GST_VIDEO_FORMAT_AYUV:
2011 case GST_VIDEO_FORMAT_UYVY:
2012 gst_base_text_overlay_shade_packed_Y (overlay, &frame,
2013 xpos, xpos + overlay->image_width,
2014 ypos, ypos + overlay->image_height);
2016 case GST_VIDEO_FORMAT_xRGB:
2017 gst_base_text_overlay_shade_xRGB (overlay, &frame,
2018 xpos, xpos + overlay->image_width,
2019 ypos, ypos + overlay->image_height);
2021 case GST_VIDEO_FORMAT_xBGR:
2022 gst_base_text_overlay_shade_xBGR (overlay, &frame,
2023 xpos, xpos + overlay->image_width,
2024 ypos, ypos + overlay->image_height);
2026 case GST_VIDEO_FORMAT_BGRx:
2027 gst_base_text_overlay_shade_BGRx (overlay, &frame,
2028 xpos, xpos + overlay->image_width,
2029 ypos, ypos + overlay->image_height);
2031 case GST_VIDEO_FORMAT_RGBx:
2032 gst_base_text_overlay_shade_RGBx (overlay, &frame,
2033 xpos, xpos + overlay->image_width,
2034 ypos, ypos + overlay->image_height);
2036 case GST_VIDEO_FORMAT_ARGB:
2037 gst_base_text_overlay_shade_ARGB (overlay, &frame,
2038 xpos, xpos + overlay->image_width,
2039 ypos, ypos + overlay->image_height);
2041 case GST_VIDEO_FORMAT_ABGR:
2042 gst_base_text_overlay_shade_ABGR (overlay, &frame,
2043 xpos, xpos + overlay->image_width,
2044 ypos, ypos + overlay->image_height);
2046 case GST_VIDEO_FORMAT_RGBA:
2047 gst_base_text_overlay_shade_RGBA (overlay, &frame,
2048 xpos, xpos + overlay->image_width,
2049 ypos, ypos + overlay->image_height);
2051 case GST_VIDEO_FORMAT_BGRA:
2052 gst_base_text_overlay_shade_BGRA (overlay, &frame,
2053 xpos, xpos + overlay->image_width,
2054 ypos, ypos + overlay->image_height);
2057 g_assert_not_reached ();
2064 if (overlay->text_image) {
2065 switch (overlay->format) {
2066 case GST_VIDEO_FORMAT_I420:
2067 gst_base_text_overlay_blit_I420 (overlay, &frame, xpos, ypos);
2069 case GST_VIDEO_FORMAT_NV12:
2070 case GST_VIDEO_FORMAT_NV21:
2071 gst_base_text_overlay_blit_NV12_NV21 (overlay, &frame, xpos, ypos);
2073 case GST_VIDEO_FORMAT_UYVY:
2074 gst_base_text_overlay_blit_UYVY (overlay, &frame, xpos, ypos);
2076 case GST_VIDEO_FORMAT_AYUV:
2077 gst_base_text_overlay_blit_AYUV (overlay, &frame, xpos, ypos);
2079 case GST_VIDEO_FORMAT_BGRx:
2080 gst_base_text_overlay_blit_BGRx (overlay, &frame, xpos, ypos);
2082 case GST_VIDEO_FORMAT_xRGB:
2083 gst_base_text_overlay_blit_xRGB (overlay, &frame, xpos, ypos);
2085 case GST_VIDEO_FORMAT_RGBx:
2086 gst_base_text_overlay_blit_RGBx (overlay, &frame, xpos, ypos);
2088 case GST_VIDEO_FORMAT_xBGR:
2089 gst_base_text_overlay_blit_xBGR (overlay, &frame, xpos, ypos);
2091 case GST_VIDEO_FORMAT_ARGB:
2092 gst_base_text_overlay_blit_ARGB (overlay, &frame, xpos, ypos);
2094 case GST_VIDEO_FORMAT_ABGR:
2095 gst_base_text_overlay_blit_ABGR (overlay, &frame, xpos, ypos);
2097 case GST_VIDEO_FORMAT_RGBA:
2098 gst_base_text_overlay_blit_RGBA (overlay, &frame, xpos, ypos);
2100 case GST_VIDEO_FORMAT_BGRA:
2101 gst_base_text_overlay_blit_BGRA (overlay, &frame, xpos, ypos);
2104 g_assert_not_reached ();
2107 gst_video_frame_unmap (&frame);
2109 return gst_pad_push (overlay->srcpad, video_frame);
2114 GST_DEBUG_OBJECT (overlay, "received invalid buffer");
2119 static GstPadLinkReturn
2120 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2122 GstBaseTextOverlay *overlay;
2124 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2125 if (G_UNLIKELY (!overlay))
2126 return GST_PAD_LINK_REFUSED;
2128 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2130 overlay->text_linked = TRUE;
2132 gst_object_unref (overlay);
2134 return GST_PAD_LINK_OK;
2138 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2140 GstBaseTextOverlay *overlay;
2142 /* don't use gst_pad_get_parent() here, will deadlock */
2143 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2145 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2147 overlay->text_linked = FALSE;
2149 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2153 gst_base_text_overlay_text_event (GstPad * pad, GstEvent * event)
2155 gboolean ret = FALSE;
2156 GstBaseTextOverlay *overlay = NULL;
2158 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2159 if (G_UNLIKELY (!overlay)) {
2160 gst_event_unref (event);
2164 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2166 switch (GST_EVENT_TYPE (event)) {
2167 case GST_EVENT_CAPS:
2171 gst_event_parse_caps (event, &caps);
2172 ret = gst_base_text_overlay_setcaps_txt (overlay, caps);
2173 gst_event_unref (event);
2176 case GST_EVENT_SEGMENT:
2178 const GstSegment *segment;
2180 overlay->text_eos = FALSE;
2182 gst_event_parse_segment (event, &segment);
2184 if (segment->format == GST_FORMAT_TIME) {
2185 GST_OBJECT_LOCK (overlay);
2186 gst_segment_copy_into (segment, &overlay->text_segment);
2187 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2188 &overlay->text_segment);
2189 GST_OBJECT_UNLOCK (overlay);
2191 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2192 ("received non-TIME newsegment event on text input"));
2195 gst_event_unref (event);
2198 /* wake up the video chain, it might be waiting for a text buffer or
2199 * a text segment update */
2200 GST_OBJECT_LOCK (overlay);
2201 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2202 GST_OBJECT_UNLOCK (overlay);
2205 case GST_EVENT_FLUSH_STOP:
2206 GST_OBJECT_LOCK (overlay);
2207 GST_INFO_OBJECT (overlay, "text flush stop");
2208 overlay->text_flushing = FALSE;
2209 overlay->text_eos = FALSE;
2210 gst_base_text_overlay_pop_text (overlay);
2211 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2212 GST_OBJECT_UNLOCK (overlay);
2213 gst_event_unref (event);
2216 case GST_EVENT_FLUSH_START:
2217 GST_OBJECT_LOCK (overlay);
2218 GST_INFO_OBJECT (overlay, "text flush start");
2219 overlay->text_flushing = TRUE;
2220 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2221 GST_OBJECT_UNLOCK (overlay);
2222 gst_event_unref (event);
2226 GST_OBJECT_LOCK (overlay);
2227 overlay->text_eos = TRUE;
2228 GST_INFO_OBJECT (overlay, "text EOS");
2229 /* wake up the video chain, it might be waiting for a text buffer or
2230 * a text segment update */
2231 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2232 GST_OBJECT_UNLOCK (overlay);
2233 gst_event_unref (event);
2237 ret = gst_pad_event_default (pad, event);
2241 gst_object_unref (overlay);
2247 gst_base_text_overlay_video_event (GstPad * pad, GstEvent * event)
2249 gboolean ret = FALSE;
2250 GstBaseTextOverlay *overlay = NULL;
2252 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2253 if (G_UNLIKELY (!overlay)) {
2254 gst_event_unref (event);
2258 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2260 switch (GST_EVENT_TYPE (event)) {
2261 case GST_EVENT_CAPS:
2265 gst_event_parse_caps (event, &caps);
2266 ret = gst_base_text_overlay_setcaps (overlay, caps);
2267 gst_event_unref (event);
2270 case GST_EVENT_SEGMENT:
2272 const GstSegment *segment;
2274 GST_DEBUG_OBJECT (overlay, "received new segment");
2276 gst_event_parse_segment (event, &segment);
2278 if (segment->format == GST_FORMAT_TIME) {
2279 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2282 gst_segment_copy_into (segment, &overlay->segment);
2284 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2285 ("received non-TIME newsegment event on video input"));
2288 ret = gst_pad_event_default (pad, event);
2292 GST_OBJECT_LOCK (overlay);
2293 GST_INFO_OBJECT (overlay, "video EOS");
2294 overlay->video_eos = TRUE;
2295 GST_OBJECT_UNLOCK (overlay);
2296 ret = gst_pad_event_default (pad, event);
2298 case GST_EVENT_FLUSH_START:
2299 GST_OBJECT_LOCK (overlay);
2300 GST_INFO_OBJECT (overlay, "video flush start");
2301 overlay->video_flushing = TRUE;
2302 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2303 GST_OBJECT_UNLOCK (overlay);
2304 ret = gst_pad_event_default (pad, event);
2306 case GST_EVENT_FLUSH_STOP:
2307 GST_OBJECT_LOCK (overlay);
2308 GST_INFO_OBJECT (overlay, "video flush stop");
2309 overlay->video_flushing = FALSE;
2310 overlay->video_eos = FALSE;
2311 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2312 GST_OBJECT_UNLOCK (overlay);
2313 ret = gst_pad_event_default (pad, event);
2316 ret = gst_pad_event_default (pad, event);
2320 gst_object_unref (overlay);
2326 gst_base_text_overlay_video_query (GstPad * pad, GstObject * parent,
2329 gboolean ret = FALSE;
2331 switch (GST_QUERY_TYPE (query)) {
2332 case GST_QUERY_CAPS:
2334 GstCaps *filter, *caps;
2336 gst_query_parse_caps (query, &filter);
2337 caps = gst_base_text_overlay_getcaps (pad, filter);
2338 gst_query_set_caps_result (query, caps);
2339 gst_caps_unref (caps);
2344 ret = gst_pad_query_default (pad, parent, query);
2351 /* Called with lock held */
2353 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2355 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2357 if (overlay->text_buffer) {
2358 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2359 overlay->text_buffer);
2360 gst_buffer_unref (overlay->text_buffer);
2361 overlay->text_buffer = NULL;
2364 /* Let the text task know we used that buffer */
2365 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2368 /* We receive text buffers here. If they are out of segment we just ignore them.
2369 If the buffer is in our segment we keep it internally except if another one
2370 is already waiting here, in that case we wait that it gets kicked out */
2371 static GstFlowReturn
2372 gst_base_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
2374 GstFlowReturn ret = GST_FLOW_OK;
2375 GstBaseTextOverlay *overlay = NULL;
2376 gboolean in_seg = FALSE;
2377 guint64 clip_start = 0, clip_stop = 0;
2379 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2381 GST_OBJECT_LOCK (overlay);
2383 if (overlay->text_flushing) {
2384 GST_OBJECT_UNLOCK (overlay);
2385 ret = GST_FLOW_WRONG_STATE;
2386 GST_LOG_OBJECT (overlay, "text flushing");
2390 if (overlay->text_eos) {
2391 GST_OBJECT_UNLOCK (overlay);
2393 GST_LOG_OBJECT (overlay, "text EOS");
2397 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2398 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2399 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2400 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2401 GST_BUFFER_DURATION (buffer)));
2403 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2406 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2407 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2409 stop = GST_CLOCK_TIME_NONE;
2411 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2412 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2418 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2419 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2420 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2421 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2423 /* Wait for the previous buffer to go away */
2424 while (overlay->text_buffer != NULL) {
2425 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2426 GST_DEBUG_PAD_NAME (pad));
2427 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2428 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2429 if (overlay->text_flushing) {
2430 GST_OBJECT_UNLOCK (overlay);
2431 ret = GST_FLOW_WRONG_STATE;
2436 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2437 overlay->text_segment.position = clip_start;
2439 overlay->text_buffer = buffer;
2440 /* That's a new text buffer we need to render */
2441 overlay->need_render = TRUE;
2443 /* in case the video chain is waiting for a text buffer, wake it up */
2444 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2447 GST_OBJECT_UNLOCK (overlay);
2454 static GstFlowReturn
2455 gst_base_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
2457 GstBaseTextOverlayClass *klass;
2458 GstBaseTextOverlay *overlay;
2459 GstFlowReturn ret = GST_FLOW_OK;
2460 gboolean in_seg = FALSE;
2461 guint64 start, stop, clip_start = 0, clip_stop = 0;
2464 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2465 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2467 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2468 goto missing_timestamp;
2470 /* ignore buffers that are outside of the current segment */
2471 start = GST_BUFFER_TIMESTAMP (buffer);
2473 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2474 stop = GST_CLOCK_TIME_NONE;
2476 stop = start + GST_BUFFER_DURATION (buffer);
2479 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2480 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2481 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2483 /* segment_clip() will adjust start unconditionally to segment_start if
2484 * no stop time is provided, so handle this ourselves */
2485 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2486 goto out_of_segment;
2488 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2489 &clip_start, &clip_stop);
2492 goto out_of_segment;
2494 /* if the buffer is only partially in the segment, fix up stamps */
2495 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2496 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2497 buffer = gst_buffer_make_writable (buffer);
2498 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2500 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2503 /* now, after we've done the clipping, fix up end time if there's no
2504 * duration (we only use those estimated values internally though, we
2505 * don't want to set bogus values on the buffer itself) */
2509 gint fps_num, fps_denom;
2511 /* FIXME, store this in setcaps */
2512 caps = gst_pad_get_current_caps (pad);
2513 s = gst_caps_get_structure (caps, 0);
2514 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2515 fps_num && fps_denom) {
2516 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2517 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2519 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2520 stop = start + 1; /* we need to assume some interval */
2522 gst_caps_unref (caps);
2525 gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2529 GST_OBJECT_LOCK (overlay);
2531 if (overlay->video_flushing)
2534 if (overlay->video_eos)
2537 if (overlay->silent) {
2538 GST_OBJECT_UNLOCK (overlay);
2539 ret = gst_pad_push (overlay->srcpad, buffer);
2541 /* Update position */
2542 overlay->segment.position = clip_start;
2547 /* Text pad not linked, rendering internal text */
2548 if (!overlay->text_linked) {
2549 if (klass->get_text) {
2550 text = klass->get_text (overlay, buffer);
2552 text = g_strdup (overlay->default_text);
2555 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2556 "text: '%s'", GST_STR_NULL (text));
2558 GST_OBJECT_UNLOCK (overlay);
2560 if (text != NULL && *text != '\0') {
2561 /* Render and push */
2562 gst_base_text_overlay_render_text (overlay, text, -1);
2563 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2565 /* Invalid or empty string */
2566 ret = gst_pad_push (overlay->srcpad, buffer);
2569 /* Text pad linked, check if we have a text buffer queued */
2570 if (overlay->text_buffer) {
2571 gboolean pop_text = FALSE, valid_text_time = TRUE;
2572 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2573 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2574 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2575 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2576 GstClockTime vid_running_time, vid_running_time_end;
2578 /* if the text buffer isn't stamped right, pop it off the
2579 * queue and display it for the current video frame only */
2580 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2581 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2582 GST_WARNING_OBJECT (overlay,
2583 "Got text buffer with invalid timestamp or duration");
2585 valid_text_time = FALSE;
2587 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2588 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2592 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2594 vid_running_time_end =
2595 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2598 /* If timestamp and duration are valid */
2599 if (valid_text_time) {
2601 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2603 text_running_time_end =
2604 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2608 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2609 GST_TIME_ARGS (text_running_time),
2610 GST_TIME_ARGS (text_running_time_end));
2611 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2612 GST_TIME_ARGS (vid_running_time),
2613 GST_TIME_ARGS (vid_running_time_end));
2615 /* Text too old or in the future */
2616 if (valid_text_time && text_running_time_end <= vid_running_time) {
2617 /* text buffer too old, get rid of it and do nothing */
2618 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2620 gst_base_text_overlay_pop_text (overlay);
2621 GST_OBJECT_UNLOCK (overlay);
2622 goto wait_for_text_buf;
2623 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2624 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2625 GST_OBJECT_UNLOCK (overlay);
2626 /* Push the video frame */
2627 ret = gst_pad_push (overlay->srcpad, buffer);
2629 gchar *in_text, *otext;
2630 gsize in_size, osize;
2633 gst_buffer_map (overlay->text_buffer, &osize, NULL, GST_MAP_READ);
2637 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2638 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2639 * here on purpose, this is something that needs fixing upstream */
2640 if (!g_utf8_validate (in_text, in_size, NULL)) {
2641 const gchar *end = NULL;
2643 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2644 in_text = g_strndup (in_text, in_size);
2645 while (!g_utf8_validate (in_text, in_size, &end) && end)
2646 *((gchar *) end) = '*';
2649 /* Get the string */
2650 if (overlay->have_pango_markup) {
2651 text = g_strndup (in_text, in_size);
2653 text = g_markup_escape_text (in_text, in_size);
2656 if (text != NULL && *text != '\0') {
2657 gint text_len = strlen (text);
2659 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2660 text[text_len - 1] == '\r')) {
2663 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2664 gst_base_text_overlay_render_text (overlay, text, text_len);
2666 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2667 gst_base_text_overlay_render_text (overlay, " ", 1);
2669 gst_buffer_unmap (overlay->text_buffer, otext, osize);
2671 if (in_text != otext)
2674 GST_OBJECT_UNLOCK (overlay);
2675 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2677 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2678 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2683 GST_OBJECT_LOCK (overlay);
2684 gst_base_text_overlay_pop_text (overlay);
2685 GST_OBJECT_UNLOCK (overlay);
2688 gboolean wait_for_text_buf = TRUE;
2690 if (overlay->text_eos)
2691 wait_for_text_buf = FALSE;
2693 if (!overlay->wait_text)
2694 wait_for_text_buf = FALSE;
2696 /* Text pad linked, but no text buffer available - what now? */
2697 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2698 GstClockTime text_start_running_time, text_position_running_time;
2699 GstClockTime vid_running_time;
2702 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2703 GST_BUFFER_TIMESTAMP (buffer));
2704 text_start_running_time =
2705 gst_segment_to_running_time (&overlay->text_segment,
2706 GST_FORMAT_TIME, overlay->text_segment.start);
2707 text_position_running_time =
2708 gst_segment_to_running_time (&overlay->text_segment,
2709 GST_FORMAT_TIME, overlay->text_segment.position);
2711 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2712 vid_running_time < text_start_running_time) ||
2713 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2714 vid_running_time < text_position_running_time)) {
2715 wait_for_text_buf = FALSE;
2719 if (wait_for_text_buf) {
2720 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2721 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2722 GST_DEBUG_OBJECT (overlay, "resuming");
2723 GST_OBJECT_UNLOCK (overlay);
2724 goto wait_for_text_buf;
2726 GST_OBJECT_UNLOCK (overlay);
2727 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2728 ret = gst_pad_push (overlay->srcpad, buffer);
2735 /* Update position */
2736 overlay->segment.position = clip_start;
2742 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2743 gst_buffer_unref (buffer);
2749 GST_OBJECT_UNLOCK (overlay);
2750 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2751 gst_buffer_unref (buffer);
2752 return GST_FLOW_WRONG_STATE;
2756 GST_OBJECT_UNLOCK (overlay);
2757 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2758 gst_buffer_unref (buffer);
2759 return GST_FLOW_EOS;
2763 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2764 gst_buffer_unref (buffer);
2769 static GstStateChangeReturn
2770 gst_base_text_overlay_change_state (GstElement * element,
2771 GstStateChange transition)
2773 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2774 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2776 switch (transition) {
2777 case GST_STATE_CHANGE_PAUSED_TO_READY:
2778 GST_OBJECT_LOCK (overlay);
2779 overlay->text_flushing = TRUE;
2780 overlay->video_flushing = TRUE;
2781 /* pop_text will broadcast on the GCond and thus also make the video
2782 * chain exit if it's waiting for a text buffer */
2783 gst_base_text_overlay_pop_text (overlay);
2784 GST_OBJECT_UNLOCK (overlay);
2790 ret = parent_class->change_state (element, transition);
2791 if (ret == GST_STATE_CHANGE_FAILURE)
2794 switch (transition) {
2795 case GST_STATE_CHANGE_READY_TO_PAUSED:
2796 GST_OBJECT_LOCK (overlay);
2797 overlay->text_flushing = FALSE;
2798 overlay->video_flushing = FALSE;
2799 overlay->video_eos = FALSE;
2800 overlay->text_eos = FALSE;
2801 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2802 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2803 GST_OBJECT_UNLOCK (overlay);
2813 plugin_init (GstPlugin * plugin)
2815 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2816 GST_TYPE_TEXT_OVERLAY) ||
2817 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2818 GST_TYPE_TIME_OVERLAY) ||
2819 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2820 GST_TYPE_CLOCK_OVERLAY) ||
2821 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2822 GST_TYPE_TEXT_RENDER)) {
2826 /*texttestsrc_plugin_init(module, plugin); */
2828 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2833 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2834 "pango", "Pango-based text rendering and overlay", plugin_init,
2835 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)