2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2003> David Schleef <ds@schleef.org>
4 * Copyright (C) <2006> Julien Moutte <julien@moutte.net>
5 * Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com>
6 * Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net>
7 * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
26 * SECTION:element-textoverlay
27 * @see_also: #GstTextRender, #GstClockOverlay, #GstTimeOverlay, #GstSubParse
29 * This plugin renders text on top of a video stream. This can be either
30 * static text or text from buffers received on the text sink pad, e.g.
31 * as produced by the subparse element. If the text sink pad is not linked,
32 * the text set via the "text" property will be rendered. If the text sink
33 * pad is linked, text will be rendered as it is received on that pad,
34 * honouring and matching the buffer timestamps of both input streams.
36 * The text can contain newline characters and text wrapping is enabled by
40 * <title>Example launch lines</title>
42 * gst-launch -v videotestsrc ! textoverlay text="Room A" valign=top halign=left ! xvimagesink
43 * ]| Here is a simple pipeline that displays a static text in the top left
44 * corner of the video picture
46 * gst-launch -v filesrc location=subtitles.srt ! subparse ! txt. videotestsrc ! timeoverlay ! textoverlay name=txt shaded-background=yes ! xvimagesink
47 * ]| Here is another pipeline that displays subtitles from an .srt subtitle
48 * file, centered at the bottom of the picture and with a rectangular shading
49 * around the text in the background:
51 * If you do not have such a subtitle file, create one looking like this
55 * 00:00:03,000 --> 00:00:05,000
59 * 00:00:08,000 --> 00:00:13,000
60 * Yes, this is a subtitle. Don't
61 * you like it? (8-13s)
64 * 00:00:18,826 --> 00:01:02,886
65 * Uh? What are you talking about?
66 * I don't understand (18-62s)
72 /* FIXME: alloc segment as part of instance struct */
78 #include <gst/video/video.h>
80 #include "gstbasetextoverlay.h"
81 #include "gsttextoverlay.h"
82 #include "gsttimeoverlay.h"
83 #include "gstclockoverlay.h"
84 #include "gsttextrender.h"
88 * - use proper strides and offset for I420
89 * - if text is wider than the video picture, it does not get
90 * clipped properly during blitting (if wrapping is disabled)
91 * - make 'shading_value' a property (or enum: light/normal/dark/verydark)?
94 GST_DEBUG_CATEGORY (pango_debug);
95 #define GST_CAT_DEFAULT pango_debug
97 #define DEFAULT_PROP_TEXT ""
98 #define DEFAULT_PROP_SHADING FALSE
99 #define DEFAULT_PROP_VALIGNMENT GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE
100 #define DEFAULT_PROP_HALIGNMENT GST_BASE_TEXT_OVERLAY_HALIGN_CENTER
101 #define DEFAULT_PROP_VALIGN "baseline"
102 #define DEFAULT_PROP_HALIGN "center"
103 #define DEFAULT_PROP_XPAD 25
104 #define DEFAULT_PROP_YPAD 25
105 #define DEFAULT_PROP_DELTAX 0
106 #define DEFAULT_PROP_DELTAY 0
107 #define DEFAULT_PROP_XPOS 0.5
108 #define DEFAULT_PROP_YPOS 0.5
109 #define DEFAULT_PROP_WRAP_MODE GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR
110 #define DEFAULT_PROP_FONT_DESC ""
111 #define DEFAULT_PROP_SILENT FALSE
112 #define DEFAULT_PROP_LINE_ALIGNMENT GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER
113 #define DEFAULT_PROP_WAIT_TEXT TRUE
114 #define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE
115 #define DEFAULT_PROP_VERTICAL_RENDER FALSE
116 #define DEFAULT_PROP_COLOR 0xffffffff
117 #define DEFAULT_PROP_OUTLINE_COLOR 0xff000000
119 /* make a property of me */
120 #define DEFAULT_SHADING_VALUE -80
122 #define MINIMUM_OUTLINE_OFFSET 1.0
123 #define DEFAULT_SCALE_BASIS 640
125 #define COMP_Y(ret, r, g, b) \
127 ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \
128 ret = CLAMP (ret, 0, 255); \
131 #define COMP_U(ret, r, g, b) \
133 ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \
134 ret = CLAMP (ret, 0, 255); \
137 #define COMP_V(ret, r, g, b) \
139 ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \
140 ret = CLAMP (ret, 0, 255); \
143 #define BLEND(ret, alpha, v0, v1) \
145 ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \
148 #define OVER(ret, alphaA, Ca, alphaB, Cb, alphaNew) \
151 _tmp = (Ca * alphaA + Cb * alphaB * (255 - alphaA) / 255) / alphaNew; \
152 ret = CLAMP (_tmp, 0, 255); \
155 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
156 # define CAIRO_ARGB_A 3
157 # define CAIRO_ARGB_R 2
158 # define CAIRO_ARGB_G 1
159 # define CAIRO_ARGB_B 0
161 # define CAIRO_ARGB_A 0
162 # define CAIRO_ARGB_R 1
163 # define CAIRO_ARGB_G 2
164 # define CAIRO_ARGB_B 3
172 PROP_VALIGN, /* deprecated */
173 PROP_HALIGN, /* deprecated */
187 PROP_AUTO_ADJUST_SIZE,
188 PROP_VERTICAL_RENDER,
195 static GstStaticPadTemplate src_template_factory =
196 GST_STATIC_PAD_TEMPLATE ("src",
199 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";"
200 GST_VIDEO_CAPS_RGBx ";"
201 GST_VIDEO_CAPS_xRGB ";"
202 GST_VIDEO_CAPS_xBGR ";"
203 GST_VIDEO_CAPS_RGBA ";"
204 GST_VIDEO_CAPS_BGRA ";"
205 GST_VIDEO_CAPS_ARGB ";"
206 GST_VIDEO_CAPS_ABGR ";"
207 GST_VIDEO_CAPS_YUV ("{AYUV, I420, UYVY, NV12, NV21}"))
210 static GstStaticPadTemplate video_sink_template_factory =
211 GST_STATIC_PAD_TEMPLATE ("video_sink",
214 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";"
215 GST_VIDEO_CAPS_RGBx ";"
216 GST_VIDEO_CAPS_xRGB ";"
217 GST_VIDEO_CAPS_xBGR ";"
218 GST_VIDEO_CAPS_RGBA ";"
219 GST_VIDEO_CAPS_BGRA ";"
220 GST_VIDEO_CAPS_ARGB ";"
221 GST_VIDEO_CAPS_ABGR ";"
222 GST_VIDEO_CAPS_YUV ("{AYUV, I420, UYVY, NV12, NV21}"))
225 #define GST_TYPE_BASE_TEXT_OVERLAY_VALIGN (gst_base_text_overlay_valign_get_type())
227 gst_base_text_overlay_valign_get_type (void)
229 static GType base_text_overlay_valign_type = 0;
230 static const GEnumValue base_text_overlay_valign[] = {
231 {GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"},
232 {GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"},
233 {GST_BASE_TEXT_OVERLAY_VALIGN_TOP, "top", "top"},
234 {GST_BASE_TEXT_OVERLAY_VALIGN_POS, "position", "position"},
235 {GST_BASE_TEXT_OVERLAY_VALIGN_CENTER, "center", "center"},
239 if (!base_text_overlay_valign_type) {
240 base_text_overlay_valign_type =
241 g_enum_register_static ("GstBaseTextOverlayVAlign",
242 base_text_overlay_valign);
244 return base_text_overlay_valign_type;
247 #define GST_TYPE_BASE_TEXT_OVERLAY_HALIGN (gst_base_text_overlay_halign_get_type())
249 gst_base_text_overlay_halign_get_type (void)
251 static GType base_text_overlay_halign_type = 0;
252 static const GEnumValue base_text_overlay_halign[] = {
253 {GST_BASE_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"},
254 {GST_BASE_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"},
255 {GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"},
256 {GST_BASE_TEXT_OVERLAY_HALIGN_POS, "position", "position"},
260 if (!base_text_overlay_halign_type) {
261 base_text_overlay_halign_type =
262 g_enum_register_static ("GstBaseTextOverlayHAlign",
263 base_text_overlay_halign);
265 return base_text_overlay_halign_type;
269 #define GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE (gst_base_text_overlay_wrap_mode_get_type())
271 gst_base_text_overlay_wrap_mode_get_type (void)
273 static GType base_text_overlay_wrap_mode_type = 0;
274 static const GEnumValue base_text_overlay_wrap_mode[] = {
275 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"},
276 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"},
277 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"},
278 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"},
282 if (!base_text_overlay_wrap_mode_type) {
283 base_text_overlay_wrap_mode_type =
284 g_enum_register_static ("GstBaseTextOverlayWrapMode",
285 base_text_overlay_wrap_mode);
287 return base_text_overlay_wrap_mode_type;
290 #define GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN (gst_base_text_overlay_line_align_get_type())
292 gst_base_text_overlay_line_align_get_type (void)
294 static GType base_text_overlay_line_align_type = 0;
295 static const GEnumValue base_text_overlay_line_align[] = {
296 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"},
297 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"},
298 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"},
302 if (!base_text_overlay_line_align_type) {
303 base_text_overlay_line_align_type =
304 g_enum_register_static ("GstBaseTextOverlayLineAlign",
305 base_text_overlay_line_align);
307 return base_text_overlay_line_align_type;
310 #define GST_BASE_TEXT_OVERLAY_GET_COND(ov) (((GstBaseTextOverlay *)ov)->cond)
311 #define GST_BASE_TEXT_OVERLAY_WAIT(ov) (g_cond_wait (GST_BASE_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov)))
312 #define GST_BASE_TEXT_OVERLAY_SIGNAL(ov) (g_cond_signal (GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
313 #define GST_BASE_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
315 static GstElementClass *parent_class = NULL;
316 static void gst_base_text_overlay_base_init (gpointer g_class);
317 static void gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass);
318 static void gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
319 GstBaseTextOverlayClass * klass);
321 static GstStateChangeReturn gst_base_text_overlay_change_state (GstElement *
322 element, GstStateChange transition);
324 static GstCaps *gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter);
325 static gboolean gst_base_text_overlay_setcaps (GstPad * pad, GstCaps * caps);
326 static gboolean gst_base_text_overlay_setcaps_txt (GstPad * pad,
328 static gboolean gst_base_text_overlay_src_event (GstPad * pad,
330 static gboolean gst_base_text_overlay_src_query (GstPad * pad,
333 static gboolean gst_base_text_overlay_video_event (GstPad * pad,
335 static GstFlowReturn gst_base_text_overlay_video_chain (GstPad * pad,
338 static gboolean gst_base_text_overlay_text_event (GstPad * pad,
340 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
342 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
344 static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
345 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
346 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
349 static void gst_base_text_overlay_finalize (GObject * object);
350 static void gst_base_text_overlay_set_property (GObject * object, guint prop_id,
351 const GValue * value, GParamSpec * pspec);
352 static void gst_base_text_overlay_get_property (GObject * object, guint prop_id,
353 GValue * value, GParamSpec * pspec);
355 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
356 PangoFontDescription * desc);
359 gst_base_text_overlay_get_type (void)
361 static GType type = 0;
363 if (g_once_init_enter ((gsize *) & type)) {
364 static const GTypeInfo info = {
365 sizeof (GstBaseTextOverlayClass),
366 (GBaseInitFunc) gst_base_text_overlay_base_init,
368 (GClassInitFunc) gst_base_text_overlay_class_init,
371 sizeof (GstBaseTextOverlay),
373 (GInstanceInitFunc) gst_base_text_overlay_init,
376 g_once_init_leave ((gsize *) & type,
377 g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTextOverlay", &info,
385 gst_base_text_overlay_get_text (GstBaseTextOverlay * overlay,
386 GstBuffer * video_frame)
388 return g_strdup (overlay->default_text);
392 gst_base_text_overlay_base_init (gpointer g_class)
394 GstBaseTextOverlayClass *klass = GST_BASE_TEXT_OVERLAY_CLASS (g_class);
395 PangoFontMap *fontmap;
397 /* Only lock for the subclasses here, the base class
398 * doesn't have this mutex yet and it's not necessary
400 if (klass->pango_lock)
401 g_mutex_lock (klass->pango_lock);
402 fontmap = pango_cairo_font_map_get_default ();
403 klass->pango_context =
404 pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
405 if (klass->pango_lock)
406 g_mutex_unlock (klass->pango_lock);
410 gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
412 GObjectClass *gobject_class;
413 GstElementClass *gstelement_class;
415 gobject_class = (GObjectClass *) klass;
416 gstelement_class = (GstElementClass *) klass;
418 parent_class = g_type_class_peek_parent (klass);
420 gobject_class->finalize = gst_base_text_overlay_finalize;
421 gobject_class->set_property = gst_base_text_overlay_set_property;
422 gobject_class->get_property = gst_base_text_overlay_get_property;
424 gst_element_class_add_pad_template (gstelement_class,
425 gst_static_pad_template_get (&src_template_factory));
426 gst_element_class_add_pad_template (gstelement_class,
427 gst_static_pad_template_get (&video_sink_template_factory));
429 gstelement_class->change_state =
430 GST_DEBUG_FUNCPTR (gst_base_text_overlay_change_state);
432 klass->pango_lock = g_mutex_new ();
434 klass->get_text = gst_base_text_overlay_get_text;
436 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
437 g_param_spec_string ("text", "text",
438 "Text to be display.", DEFAULT_PROP_TEXT,
439 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
440 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
441 g_param_spec_boolean ("shaded-background", "shaded background",
442 "Whether to shade the background under the text area",
443 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
444 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
445 g_param_spec_enum ("valignment", "vertical alignment",
446 "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
447 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
448 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
449 g_param_spec_enum ("halignment", "horizontal alignment",
450 "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
451 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
452 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
453 g_param_spec_string ("valign", "vertical alignment",
454 "Vertical alignment of the text (deprecated; use valignment)",
455 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
456 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
457 g_param_spec_string ("halign", "horizontal alignment",
458 "Horizontal alignment of the text (deprecated; use halignment)",
459 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
460 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
461 g_param_spec_int ("xpad", "horizontal paddding",
462 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
463 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
464 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
465 g_param_spec_int ("ypad", "vertical padding",
466 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
467 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
468 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
469 g_param_spec_int ("deltax", "X position modifier",
470 "Shift X position to the left or to the right. Unit is pixels.",
471 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
472 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
473 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
474 g_param_spec_int ("deltay", "Y position modifier",
475 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
476 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
478 * GstBaseTextOverlay:xpos
480 * Horizontal position of the rendered text when using positioned alignment.
484 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
485 g_param_spec_double ("xpos", "horizontal position",
486 "Horizontal position when using position alignment", 0, 1.0,
488 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
490 * GstBaseTextOverlay:ypos
492 * Vertical position of the rendered text when using positioned alignment.
496 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
497 g_param_spec_double ("ypos", "vertical position",
498 "Vertical position when using position alignment", 0, 1.0,
500 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
501 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
502 g_param_spec_enum ("wrap-mode", "wrap mode",
503 "Whether to wrap the text and if so how.",
504 GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
505 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
506 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
507 g_param_spec_string ("font-desc", "font description",
508 "Pango font description of font to be used for rendering. "
509 "See documentation of pango_font_description_from_string "
510 "for syntax.", DEFAULT_PROP_FONT_DESC,
511 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
513 * GstBaseTextOverlay:color
515 * Color of the rendered text.
519 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
520 g_param_spec_uint ("color", "Color",
521 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
523 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
525 * GstTextOverlay:outline-color
527 * Color of the outline of the rendered text.
531 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
532 g_param_spec_uint ("outline-color", "Text Outline Color",
533 "Color to use for outline the text (big-endian ARGB).", 0,
534 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
535 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
538 * GstBaseTextOverlay:line-alignment
540 * Alignment of text lines relative to each other (for multi-line text)
544 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
545 g_param_spec_enum ("line-alignment", "line alignment",
546 "Alignment of text lines relative to each other.",
547 GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
548 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
550 * GstBaseTextOverlay:silent
552 * If set, no text is rendered. Useful to switch off text rendering
553 * temporarily without removing the textoverlay element from the pipeline.
557 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
558 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
559 g_param_spec_boolean ("silent", "silent",
560 "Whether to render the text string",
562 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
564 * GstBaseTextOverlay:wait-text
566 * If set, the video will block until a subtitle is received on the text pad.
567 * If video and subtitles are sent in sync, like from the same demuxer, this
568 * property should be set.
572 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
573 g_param_spec_boolean ("wait-text", "Wait Text",
574 "Whether to wait for subtitles",
575 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
577 g_object_class_install_property (G_OBJECT_CLASS (klass),
578 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
579 "Automatically adjust font size to screen-size.",
580 DEFAULT_PROP_AUTO_ADJUST_SIZE,
581 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
583 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
584 g_param_spec_boolean ("vertical-render", "vertical render",
585 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
586 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
590 gst_base_text_overlay_finalize (GObject * object)
592 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
594 g_free (overlay->default_text);
596 if (overlay->text_image) {
597 g_free (overlay->text_image);
598 overlay->text_image = NULL;
601 if (overlay->layout) {
602 g_object_unref (overlay->layout);
603 overlay->layout = NULL;
606 if (overlay->text_buffer) {
607 gst_buffer_unref (overlay->text_buffer);
608 overlay->text_buffer = NULL;
612 g_cond_free (overlay->cond);
613 overlay->cond = NULL;
616 G_OBJECT_CLASS (parent_class)->finalize (object);
620 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
621 GstBaseTextOverlayClass * klass)
623 GstPadTemplate *template;
624 PangoFontDescription *desc;
627 template = gst_static_pad_template_get (&video_sink_template_factory);
628 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
629 gst_object_unref (template);
630 gst_pad_set_getcaps_function (overlay->video_sinkpad,
631 GST_DEBUG_FUNCPTR (gst_base_text_overlay_getcaps));
632 gst_pad_set_setcaps_function (overlay->video_sinkpad,
633 GST_DEBUG_FUNCPTR (gst_base_text_overlay_setcaps));
634 gst_pad_set_event_function (overlay->video_sinkpad,
635 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_event));
636 gst_pad_set_chain_function (overlay->video_sinkpad,
637 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
638 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
641 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
645 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
646 gst_object_unref (template);
648 gst_pad_set_setcaps_function (overlay->text_sinkpad,
649 GST_DEBUG_FUNCPTR (gst_base_text_overlay_setcaps_txt));
650 gst_pad_set_event_function (overlay->text_sinkpad,
651 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
652 gst_pad_set_chain_function (overlay->text_sinkpad,
653 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
654 gst_pad_set_link_function (overlay->text_sinkpad,
655 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
656 gst_pad_set_unlink_function (overlay->text_sinkpad,
657 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
658 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
662 template = gst_static_pad_template_get (&src_template_factory);
663 overlay->srcpad = gst_pad_new_from_template (template, "src");
664 gst_object_unref (template);
665 gst_pad_set_getcaps_function (overlay->srcpad,
666 GST_DEBUG_FUNCPTR (gst_base_text_overlay_getcaps));
667 gst_pad_set_event_function (overlay->srcpad,
668 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_event));
669 gst_pad_set_query_function (overlay->srcpad,
670 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_query));
671 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
673 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
674 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
676 pango_layout_new (GST_BASE_TEXT_OVERLAY_GET_CLASS
677 (overlay)->pango_context);
679 pango_context_get_font_description (GST_BASE_TEXT_OVERLAY_GET_CLASS
680 (overlay)->pango_context);
681 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
683 overlay->color = DEFAULT_PROP_COLOR;
684 overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
685 overlay->halign = DEFAULT_PROP_HALIGNMENT;
686 overlay->valign = DEFAULT_PROP_VALIGNMENT;
687 overlay->xpad = DEFAULT_PROP_XPAD;
688 overlay->ypad = DEFAULT_PROP_YPAD;
689 overlay->deltax = DEFAULT_PROP_DELTAX;
690 overlay->deltay = DEFAULT_PROP_DELTAY;
691 overlay->xpos = DEFAULT_PROP_XPOS;
692 overlay->ypos = DEFAULT_PROP_YPOS;
694 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
696 overlay->want_shading = DEFAULT_PROP_SHADING;
697 overlay->shading_value = DEFAULT_SHADING_VALUE;
698 overlay->silent = DEFAULT_PROP_SILENT;
699 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
700 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
702 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
703 overlay->need_render = TRUE;
704 overlay->text_image = NULL;
705 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
706 gst_base_text_overlay_update_render_mode (overlay);
711 overlay->text_buffer = NULL;
712 overlay->text_linked = FALSE;
713 overlay->cond = g_cond_new ();
714 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
715 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
719 gst_base_text_overlay_update_wrap_mode (GstBaseTextOverlay * overlay)
721 if (overlay->wrap_mode == GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE) {
722 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
723 pango_layout_set_width (overlay->layout, -1);
727 if (overlay->auto_adjust_size) {
728 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
729 if (overlay->use_vertical_render) {
730 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
734 (overlay->use_vertical_render ? overlay->height : overlay->width) *
738 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
739 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
740 pango_layout_set_width (overlay->layout, width);
741 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
746 gst_base_text_overlay_update_render_mode (GstBaseTextOverlay * overlay)
748 PangoMatrix matrix = PANGO_MATRIX_INIT;
749 PangoContext *context = pango_layout_get_context (overlay->layout);
751 if (overlay->use_vertical_render) {
752 pango_matrix_rotate (&matrix, -90);
753 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
754 pango_context_set_matrix (context, &matrix);
755 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
757 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
758 pango_context_set_matrix (context, &matrix);
759 pango_layout_set_alignment (overlay->layout, overlay->line_align);
764 gst_base_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps)
766 GstBaseTextOverlay *overlay;
767 GstStructure *structure;
769 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
771 structure = gst_caps_get_structure (caps, 0);
772 overlay->have_pango_markup =
773 gst_structure_has_name (structure, "text/x-pango-markup");
775 gst_object_unref (overlay);
780 /* FIXME: upstream nego (e.g. when the video window is resized) */
783 gst_base_text_overlay_setcaps (GstPad * pad, GstCaps * caps)
785 GstBaseTextOverlay *overlay;
786 GstStructure *structure;
787 gboolean ret = FALSE;
790 if (!GST_PAD_IS_SINK (pad))
793 g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
795 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
799 structure = gst_caps_get_structure (caps, 0);
800 fps = gst_structure_get_value (structure, "framerate");
803 && gst_video_format_parse_caps (caps, &overlay->format, &overlay->width,
805 ret = gst_pad_set_caps (overlay->srcpad, caps);
808 overlay->fps_n = gst_value_get_fraction_numerator (fps);
809 overlay->fps_d = gst_value_get_fraction_denominator (fps);
812 GST_OBJECT_LOCK (overlay);
813 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
814 gst_base_text_overlay_update_wrap_mode (overlay);
815 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
816 GST_OBJECT_UNLOCK (overlay);
819 gst_object_unref (overlay);
825 gst_base_text_overlay_set_property (GObject * object, guint prop_id,
826 const GValue * value, GParamSpec * pspec)
828 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
830 GST_OBJECT_LOCK (overlay);
833 g_free (overlay->default_text);
834 overlay->default_text = g_value_dup_string (value);
835 overlay->need_render = TRUE;
838 overlay->want_shading = g_value_get_boolean (value);
841 overlay->xpad = g_value_get_int (value);
844 overlay->ypad = g_value_get_int (value);
847 overlay->deltax = g_value_get_int (value);
850 overlay->deltay = g_value_get_int (value);
853 overlay->xpos = g_value_get_double (value);
856 overlay->ypos = g_value_get_double (value);
859 const gchar *s = g_value_get_string (value);
861 if (s && g_ascii_strcasecmp (s, "left") == 0)
862 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_LEFT;
863 else if (s && g_ascii_strcasecmp (s, "center") == 0)
864 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_CENTER;
865 else if (s && g_ascii_strcasecmp (s, "right") == 0)
866 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
868 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
873 const gchar *s = g_value_get_string (value);
875 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
876 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE;
877 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
878 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM;
879 else if (s && g_ascii_strcasecmp (s, "top") == 0)
880 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
882 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
886 case PROP_VALIGNMENT:
887 overlay->valign = g_value_get_enum (value);
889 case PROP_HALIGNMENT:
890 overlay->halign = g_value_get_enum (value);
893 overlay->wrap_mode = g_value_get_enum (value);
894 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
895 gst_base_text_overlay_update_wrap_mode (overlay);
896 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
900 PangoFontDescription *desc;
901 const gchar *fontdesc_str;
903 fontdesc_str = g_value_get_string (value);
904 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
905 desc = pango_font_description_from_string (fontdesc_str);
907 GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
908 pango_layout_set_font_description (overlay->layout, desc);
909 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
910 pango_font_description_free (desc);
912 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
915 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
919 overlay->color = g_value_get_uint (value);
921 case PROP_OUTLINE_COLOR:
922 overlay->outline_color = g_value_get_uint (value);
925 overlay->silent = g_value_get_boolean (value);
927 case PROP_LINE_ALIGNMENT:
928 overlay->line_align = g_value_get_enum (value);
929 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
930 pango_layout_set_alignment (overlay->layout,
931 (PangoAlignment) overlay->line_align);
932 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
935 overlay->wait_text = g_value_get_boolean (value);
937 case PROP_AUTO_ADJUST_SIZE:
938 overlay->auto_adjust_size = g_value_get_boolean (value);
939 overlay->need_render = TRUE;
941 case PROP_VERTICAL_RENDER:
942 overlay->use_vertical_render = g_value_get_boolean (value);
943 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
944 gst_base_text_overlay_update_render_mode (overlay);
945 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
946 overlay->need_render = TRUE;
949 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
953 overlay->need_render = TRUE;
954 GST_OBJECT_UNLOCK (overlay);
958 gst_base_text_overlay_get_property (GObject * object, guint prop_id,
959 GValue * value, GParamSpec * pspec)
961 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
963 GST_OBJECT_LOCK (overlay);
966 g_value_set_string (value, overlay->default_text);
969 g_value_set_boolean (value, overlay->want_shading);
972 g_value_set_int (value, overlay->xpad);
975 g_value_set_int (value, overlay->ypad);
978 g_value_set_int (value, overlay->deltax);
981 g_value_set_int (value, overlay->deltay);
984 g_value_set_double (value, overlay->xpos);
987 g_value_set_double (value, overlay->ypos);
989 case PROP_VALIGNMENT:
990 g_value_set_enum (value, overlay->valign);
992 case PROP_HALIGNMENT:
993 g_value_set_enum (value, overlay->halign);
996 g_value_set_enum (value, overlay->wrap_mode);
999 g_value_set_boolean (value, overlay->silent);
1001 case PROP_LINE_ALIGNMENT:
1002 g_value_set_enum (value, overlay->line_align);
1004 case PROP_WAIT_TEXT:
1005 g_value_set_boolean (value, overlay->wait_text);
1007 case PROP_AUTO_ADJUST_SIZE:
1008 g_value_set_boolean (value, overlay->auto_adjust_size);
1010 case PROP_VERTICAL_RENDER:
1011 g_value_set_boolean (value, overlay->use_vertical_render);
1014 g_value_set_uint (value, overlay->color);
1016 case PROP_OUTLINE_COLOR:
1017 g_value_set_uint (value, overlay->outline_color);
1020 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1024 overlay->need_render = TRUE;
1025 GST_OBJECT_UNLOCK (overlay);
1029 gst_base_text_overlay_src_query (GstPad * pad, GstQuery * query)
1031 gboolean ret = FALSE;
1032 GstBaseTextOverlay *overlay = NULL;
1034 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1036 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1038 gst_object_unref (overlay);
1044 gst_base_text_overlay_src_event (GstPad * pad, GstEvent * event)
1046 gboolean ret = FALSE;
1047 GstBaseTextOverlay *overlay = NULL;
1049 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1051 switch (GST_EVENT_TYPE (event)) {
1052 case GST_EVENT_SEEK:{
1055 /* We don't handle seek if we have not text pad */
1056 if (!overlay->text_linked) {
1057 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1058 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1062 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1064 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1066 /* Flush downstream, only for flushing seek */
1067 if (flags & GST_SEEK_FLAG_FLUSH)
1068 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1070 /* Mark ourself as flushing, unblock chains */
1071 GST_OBJECT_LOCK (overlay);
1072 overlay->video_flushing = TRUE;
1073 overlay->text_flushing = TRUE;
1074 gst_base_text_overlay_pop_text (overlay);
1075 GST_OBJECT_UNLOCK (overlay);
1077 /* Seek on each sink pad */
1078 gst_event_ref (event);
1079 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1081 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1083 gst_event_unref (event);
1088 if (overlay->text_linked) {
1089 gst_event_ref (event);
1090 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1091 gst_pad_push_event (overlay->text_sinkpad, event);
1093 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1099 gst_object_unref (overlay);
1105 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1107 GstBaseTextOverlay *overlay;
1111 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1113 if (pad == overlay->srcpad)
1114 otherpad = overlay->video_sinkpad;
1116 otherpad = overlay->srcpad;
1118 /* we can do what the peer can */
1119 caps = gst_pad_peer_get_caps (otherpad, filter);
1121 GstCaps *temp, *templ;
1123 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1125 /* filtered against our padtemplate */
1126 templ = gst_pad_get_pad_template_caps (otherpad);
1127 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1128 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1129 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1130 gst_caps_unref (caps);
1131 gst_caps_unref (templ);
1132 /* this is what we can do */
1135 /* no peer, our padtemplate is enough then */
1136 caps = gst_pad_get_pad_template_caps (pad);
1138 GstCaps *intersection;
1141 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1142 gst_caps_unref (caps);
1143 caps = intersection;
1147 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1149 gst_object_unref (overlay);
1155 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1156 PangoFontDescription * desc)
1158 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1159 overlay->shadow_offset = (double) (font_size) / 13.0;
1160 overlay->outline_offset = (double) (font_size) / 15.0;
1161 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1162 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1165 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1166 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1167 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1168 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1172 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1173 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1180 gint width = overlay->image_width;
1181 gint height = overlay->image_height;
1187 if (xpos + width > overlay->width) {
1188 width = overlay->width - xpos;
1191 if (ypos + height > overlay->height) {
1192 height = overlay->height - ypos;
1195 dest += (ypos / 1) * dest_stride;
1197 for (i = 0; i < height; i++) {
1198 pimage = text_image + 4 * (i * overlay->image_width);
1199 py = dest + i * dest_stride + xpos;
1200 for (j = 0; j < width; j++) {
1201 b = pimage[CAIRO_ARGB_B];
1202 g = pimage[CAIRO_ARGB_G];
1203 r = pimage[CAIRO_ARGB_R];
1204 a = pimage[CAIRO_ARGB_A];
1205 CAIRO_UNPREMULTIPLY (a, r, g, b);
1212 COMP_Y (y, r, g, b);
1214 BLEND (*py++, a, y, x);
1220 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1221 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1222 guint destcb_stride, guint destcr_stride, guint pix_stride)
1227 gushort r1, g1, b1, a1;
1228 guchar *pimage1, *pimage2;
1230 gint width = overlay->image_width - 2;
1231 gint height = overlay->image_height - 2;
1239 if (xpos + width > overlay->width) {
1240 width = overlay->width - xpos;
1243 if (ypos + height > overlay->height) {
1244 height = overlay->height - ypos;
1247 destcb += (ypos / 2) * destcb_stride;
1248 destcr += (ypos / 2) * destcr_stride;
1250 for (i = 0; i < height; i += 2) {
1251 pimage1 = text_image + 4 * (i * overlay->image_width);
1252 pimage2 = pimage1 + 4 * overlay->image_width;
1253 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1254 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1255 for (j = 0; j < width; j += 2) {
1256 b = pimage1[CAIRO_ARGB_B];
1257 g = pimage1[CAIRO_ARGB_G];
1258 r = pimage1[CAIRO_ARGB_R];
1259 a = pimage1[CAIRO_ARGB_A];
1260 CAIRO_UNPREMULTIPLY (a, r, g, b);
1263 b1 = pimage1[CAIRO_ARGB_B];
1264 g1 = pimage1[CAIRO_ARGB_G];
1265 r1 = pimage1[CAIRO_ARGB_R];
1266 a1 = pimage1[CAIRO_ARGB_A];
1267 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1274 b1 = pimage2[CAIRO_ARGB_B];
1275 g1 = pimage2[CAIRO_ARGB_G];
1276 r1 = pimage2[CAIRO_ARGB_R];
1277 a1 = pimage2[CAIRO_ARGB_A];
1278 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1285 /* + 2 for rounding */
1286 b1 = pimage2[CAIRO_ARGB_B];
1287 g1 = pimage2[CAIRO_ARGB_G];
1288 r1 = pimage2[CAIRO_ARGB_R];
1289 a1 = pimage2[CAIRO_ARGB_A];
1290 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1307 COMP_U (cb, r, g, b);
1308 COMP_V (cr, r, g, b);
1311 BLEND (*pcb, a, cb, x);
1313 BLEND (*pcr, a, cr, x);
1322 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1323 const gchar * string, gint textlen)
1326 cairo_surface_t *surface;
1327 PangoRectangle ink_rect, logical_rect;
1328 cairo_matrix_t cairo_matrix;
1330 double scalef = 1.0;
1333 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1335 if (overlay->auto_adjust_size) {
1336 /* 640 pixel is default */
1337 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1339 pango_layout_set_width (overlay->layout, -1);
1340 /* set text on pango layout */
1341 pango_layout_set_markup (overlay->layout, string, textlen);
1343 /* get subtitle image size */
1344 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1346 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1348 if (width + overlay->deltax >
1349 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1351 * subtitle image width is larger then overlay width
1352 * so rearrange overlay wrap mode.
1354 gst_base_text_overlay_update_wrap_mode (overlay);
1355 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1356 width = overlay->width;
1360 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1361 if (height > overlay->height) {
1362 height = overlay->height;
1364 if (overlay->use_vertical_render) {
1365 PangoRectangle rect;
1366 PangoContext *context;
1367 PangoMatrix matrix = PANGO_MATRIX_INIT;
1370 context = pango_layout_get_context (overlay->layout);
1372 pango_matrix_rotate (&matrix, -90);
1374 rect.x = rect.y = 0;
1376 rect.height = height;
1377 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1378 matrix.x0 = -rect.x;
1379 matrix.y0 = -rect.y;
1381 pango_context_set_matrix (context, &matrix);
1383 cairo_matrix.xx = matrix.xx;
1384 cairo_matrix.yx = matrix.yx;
1385 cairo_matrix.xy = matrix.xy;
1386 cairo_matrix.yy = matrix.yy;
1387 cairo_matrix.x0 = matrix.x0;
1388 cairo_matrix.y0 = matrix.y0;
1389 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1395 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1398 /* reallocate surface */
1399 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1401 surface = cairo_image_surface_create_for_data (overlay->text_image,
1402 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1403 cr = cairo_create (surface);
1406 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1409 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1411 if (overlay->want_shading)
1412 cairo_paint_with_alpha (cr, overlay->shading_value);
1414 /* apply transformations */
1415 cairo_set_matrix (cr, &cairo_matrix);
1417 /* FIXME: We use show_layout everywhere except for the surface
1418 * because it's really faster and internally does all kinds of
1419 * caching. Unfortunately we have to paint to a cairo path for
1420 * the outline and this is slow. Once Pango supports user fonts
1421 * we should use them, see
1422 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1424 * Idea would the be, to create a cairo user font that
1425 * does shadow, outline, text painting in the
1426 * render_glyph function.
1429 /* draw shadow text */
1431 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1432 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1433 pango_cairo_show_layout (cr, overlay->layout);
1436 a = (overlay->outline_color >> 24) & 0xff;
1437 r = (overlay->outline_color >> 16) & 0xff;
1438 g = (overlay->outline_color >> 8) & 0xff;
1439 b = (overlay->outline_color >> 0) & 0xff;
1441 /* draw outline text */
1443 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1444 cairo_set_line_width (cr, overlay->outline_offset);
1445 pango_cairo_layout_path (cr, overlay->layout);
1449 a = (overlay->color >> 24) & 0xff;
1450 r = (overlay->color >> 16) & 0xff;
1451 g = (overlay->color >> 8) & 0xff;
1452 b = (overlay->color >> 0) & 0xff;
1456 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1457 pango_cairo_show_layout (cr, overlay->layout);
1461 cairo_surface_destroy (surface);
1462 overlay->image_width = width;
1463 overlay->image_height = height;
1464 overlay->baseline_y = ink_rect.y;
1465 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1472 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1473 guchar * dest, gint x0, gint x1, gint y0, gint y1)
1475 gint i, j, dest_stride;
1477 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1480 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1481 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1483 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1484 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1486 for (i = y0; i < y1; ++i) {
1487 for (j = x0; j < x1; ++j) {
1488 gint y = dest[(i * dest_stride) + j] + overlay->shading_value;
1490 dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1496 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1497 guchar * dest, gint x0, gint x1, gint y0, gint y1)
1500 guint dest_stride, pixel_stride, component_offset;
1502 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1504 pixel_stride = gst_video_format_get_pixel_stride (overlay->format, 0);
1506 gst_video_format_get_component_offset (overlay->format, 0, overlay->width,
1509 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1510 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1512 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1513 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1516 x0 = gst_video_format_get_component_width (overlay->format, 0, x0);
1518 x1 = gst_video_format_get_component_width (overlay->format, 0, x1);
1521 y0 = gst_video_format_get_component_height (overlay->format, 0, y0);
1523 y1 = gst_video_format_get_component_height (overlay->format, 0, y1);
1525 for (i = y0; i < y1; i++) {
1526 for (j = x0; j < x1; j++) {
1530 y_pos = (i * dest_stride) + j * pixel_stride + component_offset;
1531 y = dest[y_pos] + overlay->shading_value;
1533 dest[y_pos] = CLAMP (y, 0, 255);
1538 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1539 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1540 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1542 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay, guchar * dest,
1543 gint x0, gint x1, gint y0, gint y1)
1547 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1548 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1550 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1551 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1553 for (i = y0; i < y1; i++) {
1554 for (j = x0; j < x1; j++) {
1557 y_pos = (i * 4 * overlay->width) + j * 4;
1558 for (k = 0; k < 4; k++) {
1559 y = dest[y_pos + k] + overlay->shading_value;
1560 dest[y_pos + k] = CLAMP (y, 0, 255);
1566 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1567 static inline void \
1568 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, guchar * dest, \
1569 gint x0, gint x1, gint y0, gint y1) \
1573 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1574 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1576 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1577 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1579 for (i = y0; i < y1; i++) {\
1580 for (j = x0; j < x1; j++) {\
1582 y_pos = (i * 4 * overlay->width) + j * 4;\
1583 for (k = OFFSET; k < 3+OFFSET; k++) {\
1584 y = dest[y_pos + k] + overlay->shading_value;\
1585 dest[y_pos + k] = CLAMP (y, 0, 255);\
1590 ARGB_SHADE_FUNCTION (ARGB, 1);
1591 ARGB_SHADE_FUNCTION (ABGR, 1);
1592 ARGB_SHADE_FUNCTION (RGBA, 0);
1593 ARGB_SHADE_FUNCTION (BGRA, 0);
1597 * - use proper strides and offset for I420
1598 * - don't draw over the edge of the picture (try a longer
1599 * text with a huge font size)
1603 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1604 guint8 * yuv_pixels, gint xpos, gint ypos)
1606 int y_stride, uv_stride;
1607 int u_offset, v_offset;
1610 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1611 * to a boundary of integer number of U/V pixels:
1613 xpos = GST_ROUND_UP_2 (xpos);
1614 ypos = GST_ROUND_UP_2 (ypos);
1617 h = overlay->height;
1619 y_stride = gst_video_format_get_row_stride (overlay->format, 0, w);
1620 uv_stride = gst_video_format_get_row_stride (overlay->format, 1, w);
1621 u_offset = gst_video_format_get_component_offset (overlay->format, 1, w, h);
1622 v_offset = gst_video_format_get_component_offset (overlay->format, 2, w, h);
1624 gst_base_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos,
1625 overlay->text_image, y_stride);
1626 gst_base_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1627 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, uv_stride,
1632 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1633 guint8 * yuv_pixels, gint xpos, gint ypos)
1635 int y_stride, u_stride, v_stride;
1636 int u_offset, v_offset;
1639 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1640 * to a boundary of integer number of U/V pixels:
1642 xpos = GST_ROUND_UP_2 (xpos);
1643 ypos = GST_ROUND_UP_2 (ypos);
1646 h = overlay->height;
1648 y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, w);
1649 u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, w);
1650 v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, w);
1652 gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1, w, h);
1654 gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h);
1656 gst_base_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos,
1657 overlay->text_image, y_stride);
1658 gst_base_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1659 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride,
1664 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1665 guint8 * yuv_pixels, gint xpos, gint ypos)
1672 guchar *pimage, *dest;
1674 /* because U/V is 2x horizontally subsampled, we need to round to a
1675 * boundary of integer number of U/V pixels in x dimension:
1677 xpos = GST_ROUND_UP_2 (xpos);
1679 w = overlay->image_width - 2;
1680 h = overlay->image_height - 2;
1686 if (xpos + w > overlay->width) {
1687 w = overlay->width - xpos;
1690 if (ypos + h > overlay->height) {
1691 h = overlay->height - ypos;
1694 for (i = 0; i < h; i++) {
1695 pimage = overlay->text_image + i * overlay->image_width * 4;
1696 dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1697 for (j = 0; j < w; j += 2) {
1698 b0 = pimage[CAIRO_ARGB_B];
1699 g0 = pimage[CAIRO_ARGB_G];
1700 r0 = pimage[CAIRO_ARGB_R];
1701 a0 = pimage[CAIRO_ARGB_A];
1702 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1705 b1 = pimage[CAIRO_ARGB_B];
1706 g1 = pimage[CAIRO_ARGB_G];
1707 r1 = pimage[CAIRO_ARGB_R];
1708 a1 = pimage[CAIRO_ARGB_A];
1709 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1719 COMP_Y (y0, r0, g0, b0);
1720 COMP_Y (y1, r1, g1, b1);
1730 COMP_U (u, r0, g0, b0);
1731 COMP_V (v, r0, g0, b0);
1733 BLEND (*dest, a0, u, *dest);
1735 BLEND (*dest, a0, y0, *dest);
1737 BLEND (*dest, a0, v, *dest);
1739 BLEND (*dest, a0, y1, *dest);
1746 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1747 guint8 * rgb_pixels, gint xpos, gint ypos)
1753 guchar *pimage, *dest;
1755 w = overlay->image_width;
1756 h = overlay->image_height;
1762 if (xpos + w > overlay->width) {
1763 w = overlay->width - xpos;
1766 if (ypos + h > overlay->height) {
1767 h = overlay->height - ypos;
1770 for (i = 0; i < h; i++) {
1771 pimage = overlay->text_image + i * overlay->image_width * 4;
1772 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1773 for (j = 0; j < w; j++) {
1774 a = pimage[CAIRO_ARGB_A];
1775 b = pimage[CAIRO_ARGB_B];
1776 g = pimage[CAIRO_ARGB_G];
1777 r = pimage[CAIRO_ARGB_R];
1779 CAIRO_UNPREMULTIPLY (a, r, g, b);
1781 // convert background to yuv
1782 COMP_Y (y, r, g, b);
1783 COMP_U (u, r, g, b);
1784 COMP_V (v, r, g, b);
1786 // preform text "OVER" background alpha compositing
1787 a1 = a + (dest[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1788 OVER (dest[1], a, y, dest[0], dest[1], a1);
1789 OVER (dest[2], a, u, dest[0], dest[2], a1);
1790 OVER (dest[3], a, v, dest[0], dest[3], a1);
1791 dest[0] = a1 - 1; // remove the temporary 1 we added
1799 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1800 static inline void \
1801 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1802 guint8 * rgb_pixels, gint xpos, gint ypos) \
1807 guchar *pimage, *dest; \
1809 w = overlay->image_width; \
1810 h = overlay->image_height; \
1816 if (xpos + w > overlay->width) { \
1817 w = overlay->width - xpos; \
1820 if (ypos + h > overlay->height) { \
1821 h = overlay->height - ypos; \
1824 for (i = 0; i < h; i++) { \
1825 pimage = overlay->text_image + i * overlay->image_width * 4; \
1826 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1827 for (j = 0; j < w; j++) { \
1828 a = pimage[CAIRO_ARGB_A]; \
1829 b = pimage[CAIRO_ARGB_B]; \
1830 g = pimage[CAIRO_ARGB_G]; \
1831 r = pimage[CAIRO_ARGB_R]; \
1832 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1833 b = (b*a + dest[B] * (255-a)) / 255; \
1834 g = (g*a + dest[G] * (255-a)) / 255; \
1835 r = (r*a + dest[R] * (255-a)) / 255; \
1845 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1846 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1847 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1848 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1850 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1851 static inline void \
1852 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1853 guint8 * rgb_pixels, gint xpos, gint ypos) \
1855 int a, r, g, b, a1; \
1858 guchar *pimage, *dest; \
1860 w = overlay->image_width; \
1861 h = overlay->image_height; \
1867 if (xpos + w > overlay->width) { \
1868 w = overlay->width - xpos; \
1871 if (ypos + h > overlay->height) { \
1872 h = overlay->height - ypos; \
1875 for (i = 0; i < h; i++) { \
1876 pimage = overlay->text_image + i * overlay->image_width * 4; \
1877 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1878 for (j = 0; j < w; j++) { \
1879 a = pimage[CAIRO_ARGB_A]; \
1880 b = pimage[CAIRO_ARGB_B]; \
1881 g = pimage[CAIRO_ARGB_G]; \
1882 r = pimage[CAIRO_ARGB_R]; \
1883 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1884 a1 = a + (dest[A] * (255 - a)) / 255 + 1; \
1885 OVER (dest[R], a, r, dest[0], dest[R], a1); \
1886 OVER (dest[G], a, g, dest[0], dest[G], a1); \
1887 OVER (dest[B], a, b, dest[0], dest[B], a1); \
1894 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1895 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1896 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1897 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1900 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1901 const gchar * text, gint textlen)
1905 if (!overlay->need_render) {
1906 GST_DEBUG ("Using previously rendered text.");
1910 /* -1 is the whole string */
1911 if (text != NULL && textlen < 0) {
1912 textlen = strlen (text);
1916 string = g_strndup (text, textlen);
1917 } else { /* empty string */
1918 string = g_strdup (" ");
1920 g_strdelimit (string, "\r\t", ' ');
1921 textlen = strlen (string);
1923 /* FIXME: should we check for UTF-8 here? */
1925 GST_DEBUG ("Rendering '%s'", string);
1926 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1930 overlay->need_render = FALSE;
1933 static GstFlowReturn
1934 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1935 GstBuffer * video_frame)
1939 GstBaseTextOverlayVAlign valign;
1940 GstBaseTextOverlayHAlign halign;
1944 width = overlay->image_width;
1945 height = overlay->image_height;
1947 video_frame = gst_buffer_make_writable (video_frame);
1949 data = gst_buffer_map (video_frame, &size, NULL, GST_MAP_WRITE);
1951 if (overlay->use_vertical_render)
1952 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1954 halign = overlay->halign;
1957 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1958 xpos = overlay->xpad;
1960 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1961 xpos = (overlay->width - width) / 2;
1963 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1964 xpos = overlay->width - width - overlay->xpad;
1966 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1967 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1968 xpos = CLAMP (xpos, 0, overlay->width - width);
1975 xpos += overlay->deltax;
1977 if (overlay->use_vertical_render)
1978 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1980 valign = overlay->valign;
1983 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1984 ypos = overlay->height - height - overlay->ypad;
1986 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1987 ypos = overlay->height - (height + overlay->ypad);
1989 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1990 ypos = overlay->ypad;
1992 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1993 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1994 ypos = CLAMP (ypos, 0, overlay->height - height);
1996 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1997 ypos = (overlay->height - height) / 2;
2000 ypos = overlay->ypad;
2003 ypos += overlay->deltay;
2005 /* shaded background box */
2006 if (overlay->want_shading) {
2007 switch (overlay->format) {
2008 case GST_VIDEO_FORMAT_I420:
2009 case GST_VIDEO_FORMAT_NV12:
2010 case GST_VIDEO_FORMAT_NV21:
2011 gst_base_text_overlay_shade_planar_Y (overlay, data,
2012 xpos, xpos + overlay->image_width,
2013 ypos, ypos + overlay->image_height);
2015 case GST_VIDEO_FORMAT_AYUV:
2016 case GST_VIDEO_FORMAT_UYVY:
2017 gst_base_text_overlay_shade_packed_Y (overlay, data,
2018 xpos, xpos + overlay->image_width,
2019 ypos, ypos + overlay->image_height);
2021 case GST_VIDEO_FORMAT_xRGB:
2022 gst_base_text_overlay_shade_xRGB (overlay, data,
2023 xpos, xpos + overlay->image_width,
2024 ypos, ypos + overlay->image_height);
2026 case GST_VIDEO_FORMAT_xBGR:
2027 gst_base_text_overlay_shade_xBGR (overlay, data,
2028 xpos, xpos + overlay->image_width,
2029 ypos, ypos + overlay->image_height);
2031 case GST_VIDEO_FORMAT_BGRx:
2032 gst_base_text_overlay_shade_BGRx (overlay, data,
2033 xpos, xpos + overlay->image_width,
2034 ypos, ypos + overlay->image_height);
2036 case GST_VIDEO_FORMAT_RGBx:
2037 gst_base_text_overlay_shade_RGBx (overlay, data,
2038 xpos, xpos + overlay->image_width,
2039 ypos, ypos + overlay->image_height);
2041 case GST_VIDEO_FORMAT_ARGB:
2042 gst_base_text_overlay_shade_ARGB (overlay, data,
2043 xpos, xpos + overlay->image_width,
2044 ypos, ypos + overlay->image_height);
2046 case GST_VIDEO_FORMAT_ABGR:
2047 gst_base_text_overlay_shade_ABGR (overlay, data,
2048 xpos, xpos + overlay->image_width,
2049 ypos, ypos + overlay->image_height);
2051 case GST_VIDEO_FORMAT_RGBA:
2052 gst_base_text_overlay_shade_RGBA (overlay, data,
2053 xpos, xpos + overlay->image_width,
2054 ypos, ypos + overlay->image_height);
2056 case GST_VIDEO_FORMAT_BGRA:
2057 gst_base_text_overlay_shade_BGRA (overlay, data,
2058 xpos, xpos + overlay->image_width,
2059 ypos, ypos + overlay->image_height);
2062 g_assert_not_reached ();
2069 if (overlay->text_image) {
2070 switch (overlay->format) {
2071 case GST_VIDEO_FORMAT_I420:
2072 gst_base_text_overlay_blit_I420 (overlay, data, xpos, ypos);
2074 case GST_VIDEO_FORMAT_NV12:
2075 case GST_VIDEO_FORMAT_NV21:
2076 gst_base_text_overlay_blit_NV12_NV21 (overlay, data, xpos, ypos);
2078 case GST_VIDEO_FORMAT_UYVY:
2079 gst_base_text_overlay_blit_UYVY (overlay, data, xpos, ypos);
2081 case GST_VIDEO_FORMAT_AYUV:
2082 gst_base_text_overlay_blit_AYUV (overlay, data, xpos, ypos);
2084 case GST_VIDEO_FORMAT_BGRx:
2085 gst_base_text_overlay_blit_BGRx (overlay, data, xpos, ypos);
2087 case GST_VIDEO_FORMAT_xRGB:
2088 gst_base_text_overlay_blit_xRGB (overlay, data, xpos, ypos);
2090 case GST_VIDEO_FORMAT_RGBx:
2091 gst_base_text_overlay_blit_RGBx (overlay, data, xpos, ypos);
2093 case GST_VIDEO_FORMAT_xBGR:
2094 gst_base_text_overlay_blit_xBGR (overlay, data, xpos, ypos);
2096 case GST_VIDEO_FORMAT_ARGB:
2097 gst_base_text_overlay_blit_ARGB (overlay, data, xpos, ypos);
2099 case GST_VIDEO_FORMAT_ABGR:
2100 gst_base_text_overlay_blit_ABGR (overlay, data, xpos, ypos);
2102 case GST_VIDEO_FORMAT_RGBA:
2103 gst_base_text_overlay_blit_RGBA (overlay, data, xpos, ypos);
2105 case GST_VIDEO_FORMAT_BGRA:
2106 gst_base_text_overlay_blit_BGRA (overlay, data, xpos, ypos);
2109 g_assert_not_reached ();
2112 gst_buffer_unmap (video_frame, data, size);
2114 return gst_pad_push (overlay->srcpad, video_frame);
2117 static GstPadLinkReturn
2118 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2120 GstBaseTextOverlay *overlay;
2122 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2124 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2126 overlay->text_linked = TRUE;
2128 gst_object_unref (overlay);
2130 return GST_PAD_LINK_OK;
2134 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2136 GstBaseTextOverlay *overlay;
2138 /* don't use gst_pad_get_parent() here, will deadlock */
2139 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2141 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2143 overlay->text_linked = FALSE;
2145 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2149 gst_base_text_overlay_text_event (GstPad * pad, GstEvent * event)
2151 gboolean ret = FALSE;
2152 GstBaseTextOverlay *overlay = NULL;
2154 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2156 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2158 switch (GST_EVENT_TYPE (event)) {
2159 case GST_EVENT_SEGMENT:
2161 const GstSegment *segment;
2163 overlay->text_eos = FALSE;
2165 gst_event_parse_segment (event, &segment);
2167 if (segment->format == GST_FORMAT_TIME) {
2168 GST_OBJECT_LOCK (overlay);
2169 gst_segment_copy_into (segment, &overlay->text_segment);
2170 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2171 &overlay->text_segment);
2172 GST_OBJECT_UNLOCK (overlay);
2174 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2175 ("received non-TIME newsegment event on text input"));
2178 gst_event_unref (event);
2181 /* wake up the video chain, it might be waiting for a text buffer or
2182 * a text segment update */
2183 GST_OBJECT_LOCK (overlay);
2184 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2185 GST_OBJECT_UNLOCK (overlay);
2188 case GST_EVENT_FLUSH_STOP:
2189 GST_OBJECT_LOCK (overlay);
2190 GST_INFO_OBJECT (overlay, "text flush stop");
2191 overlay->text_flushing = FALSE;
2192 overlay->text_eos = FALSE;
2193 gst_base_text_overlay_pop_text (overlay);
2194 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2195 GST_OBJECT_UNLOCK (overlay);
2196 gst_event_unref (event);
2199 case GST_EVENT_FLUSH_START:
2200 GST_OBJECT_LOCK (overlay);
2201 GST_INFO_OBJECT (overlay, "text flush start");
2202 overlay->text_flushing = TRUE;
2203 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2204 GST_OBJECT_UNLOCK (overlay);
2205 gst_event_unref (event);
2209 GST_OBJECT_LOCK (overlay);
2210 overlay->text_eos = TRUE;
2211 GST_INFO_OBJECT (overlay, "text EOS");
2212 /* wake up the video chain, it might be waiting for a text buffer or
2213 * a text segment update */
2214 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2215 GST_OBJECT_UNLOCK (overlay);
2216 gst_event_unref (event);
2220 ret = gst_pad_event_default (pad, event);
2224 gst_object_unref (overlay);
2230 gst_base_text_overlay_video_event (GstPad * pad, GstEvent * event)
2232 gboolean ret = FALSE;
2233 GstBaseTextOverlay *overlay = NULL;
2235 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2237 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2239 switch (GST_EVENT_TYPE (event)) {
2240 case GST_EVENT_SEGMENT:
2242 const GstSegment *segment;
2244 GST_DEBUG_OBJECT (overlay, "received new segment");
2246 gst_event_parse_segment (event, &segment);
2248 if (segment->format == GST_FORMAT_TIME) {
2249 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2252 gst_segment_copy_into (segment, &overlay->segment);
2254 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2255 ("received non-TIME newsegment event on video input"));
2258 ret = gst_pad_event_default (pad, event);
2262 GST_OBJECT_LOCK (overlay);
2263 GST_INFO_OBJECT (overlay, "video EOS");
2264 overlay->video_eos = TRUE;
2265 GST_OBJECT_UNLOCK (overlay);
2266 ret = gst_pad_event_default (pad, event);
2268 case GST_EVENT_FLUSH_START:
2269 GST_OBJECT_LOCK (overlay);
2270 GST_INFO_OBJECT (overlay, "video flush start");
2271 overlay->video_flushing = TRUE;
2272 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2273 GST_OBJECT_UNLOCK (overlay);
2274 ret = gst_pad_event_default (pad, event);
2276 case GST_EVENT_FLUSH_STOP:
2277 GST_OBJECT_LOCK (overlay);
2278 GST_INFO_OBJECT (overlay, "video flush stop");
2279 overlay->video_flushing = FALSE;
2280 overlay->video_eos = FALSE;
2281 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2282 GST_OBJECT_UNLOCK (overlay);
2283 ret = gst_pad_event_default (pad, event);
2286 ret = gst_pad_event_default (pad, event);
2290 gst_object_unref (overlay);
2295 /* Called with lock held */
2297 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2299 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2301 if (overlay->text_buffer) {
2302 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2303 overlay->text_buffer);
2304 gst_buffer_unref (overlay->text_buffer);
2305 overlay->text_buffer = NULL;
2308 /* Let the text task know we used that buffer */
2309 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2312 /* We receive text buffers here. If they are out of segment we just ignore them.
2313 If the buffer is in our segment we keep it internally except if another one
2314 is already waiting here, in that case we wait that it gets kicked out */
2315 static GstFlowReturn
2316 gst_base_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
2318 GstFlowReturn ret = GST_FLOW_OK;
2319 GstBaseTextOverlay *overlay = NULL;
2320 gboolean in_seg = FALSE;
2321 guint64 clip_start = 0, clip_stop = 0;
2323 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2325 GST_OBJECT_LOCK (overlay);
2327 if (overlay->text_flushing) {
2328 GST_OBJECT_UNLOCK (overlay);
2329 ret = GST_FLOW_WRONG_STATE;
2330 GST_LOG_OBJECT (overlay, "text flushing");
2334 if (overlay->text_eos) {
2335 GST_OBJECT_UNLOCK (overlay);
2336 ret = GST_FLOW_UNEXPECTED;
2337 GST_LOG_OBJECT (overlay, "text EOS");
2341 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2342 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2343 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2344 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2345 GST_BUFFER_DURATION (buffer)));
2347 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2350 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2351 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2353 stop = GST_CLOCK_TIME_NONE;
2355 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2356 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2362 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2363 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2364 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2365 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2367 /* Wait for the previous buffer to go away */
2368 while (overlay->text_buffer != NULL) {
2369 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2370 GST_DEBUG_PAD_NAME (pad));
2371 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2372 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2373 if (overlay->text_flushing) {
2374 GST_OBJECT_UNLOCK (overlay);
2375 ret = GST_FLOW_WRONG_STATE;
2380 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2381 overlay->text_segment.position = clip_start;
2383 overlay->text_buffer = buffer;
2384 /* That's a new text buffer we need to render */
2385 overlay->need_render = TRUE;
2387 /* in case the video chain is waiting for a text buffer, wake it up */
2388 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2391 GST_OBJECT_UNLOCK (overlay);
2398 static GstFlowReturn
2399 gst_base_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
2401 GstBaseTextOverlayClass *klass;
2402 GstBaseTextOverlay *overlay;
2403 GstFlowReturn ret = GST_FLOW_OK;
2404 gboolean in_seg = FALSE;
2405 guint64 start, stop, clip_start = 0, clip_stop = 0;
2408 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2409 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2411 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2412 goto missing_timestamp;
2414 /* ignore buffers that are outside of the current segment */
2415 start = GST_BUFFER_TIMESTAMP (buffer);
2417 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2418 stop = GST_CLOCK_TIME_NONE;
2420 stop = start + GST_BUFFER_DURATION (buffer);
2423 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2424 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2425 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2427 /* segment_clip() will adjust start unconditionally to segment_start if
2428 * no stop time is provided, so handle this ourselves */
2429 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2430 goto out_of_segment;
2432 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2433 &clip_start, &clip_stop);
2436 goto out_of_segment;
2438 /* if the buffer is only partially in the segment, fix up stamps */
2439 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2440 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2441 buffer = gst_buffer_make_writable (buffer);
2442 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2444 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2447 /* now, after we've done the clipping, fix up end time if there's no
2448 * duration (we only use those estimated values internally though, we
2449 * don't want to set bogus values on the buffer itself) */
2453 gint fps_num, fps_denom;
2455 /* FIXME, store this in setcaps */
2456 caps = gst_pad_get_current_caps (pad);
2457 s = gst_caps_get_structure (caps, 0);
2458 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2459 fps_num && fps_denom) {
2460 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2461 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2463 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2464 stop = start + 1; /* we need to assume some interval */
2466 gst_caps_unref (caps);
2469 gst_object_sync_values (G_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2473 GST_OBJECT_LOCK (overlay);
2475 if (overlay->video_flushing)
2478 if (overlay->video_eos)
2481 if (overlay->silent) {
2482 GST_OBJECT_UNLOCK (overlay);
2483 ret = gst_pad_push (overlay->srcpad, buffer);
2485 /* Update position */
2486 overlay->segment.position = clip_start;
2491 /* Text pad not linked, rendering internal text */
2492 if (!overlay->text_linked) {
2493 if (klass->get_text) {
2494 text = klass->get_text (overlay, buffer);
2496 text = g_strdup (overlay->default_text);
2499 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2500 "text: '%s'", GST_STR_NULL (text));
2502 GST_OBJECT_UNLOCK (overlay);
2504 if (text != NULL && *text != '\0') {
2505 /* Render and push */
2506 gst_base_text_overlay_render_text (overlay, text, -1);
2507 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2509 /* Invalid or empty string */
2510 ret = gst_pad_push (overlay->srcpad, buffer);
2513 /* Text pad linked, check if we have a text buffer queued */
2514 if (overlay->text_buffer) {
2515 gboolean pop_text = FALSE, valid_text_time = TRUE;
2516 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2517 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2518 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2519 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2520 GstClockTime vid_running_time, vid_running_time_end;
2522 /* if the text buffer isn't stamped right, pop it off the
2523 * queue and display it for the current video frame only */
2524 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2525 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2526 GST_WARNING_OBJECT (overlay,
2527 "Got text buffer with invalid timestamp or duration");
2529 valid_text_time = FALSE;
2531 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2532 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2536 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2538 vid_running_time_end =
2539 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2542 /* If timestamp and duration are valid */
2543 if (valid_text_time) {
2545 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2547 text_running_time_end =
2548 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2552 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2553 GST_TIME_ARGS (text_running_time),
2554 GST_TIME_ARGS (text_running_time_end));
2555 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2556 GST_TIME_ARGS (vid_running_time),
2557 GST_TIME_ARGS (vid_running_time_end));
2559 /* Text too old or in the future */
2560 if (valid_text_time && text_running_time_end <= vid_running_time) {
2561 /* text buffer too old, get rid of it and do nothing */
2562 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2564 gst_base_text_overlay_pop_text (overlay);
2565 GST_OBJECT_UNLOCK (overlay);
2566 goto wait_for_text_buf;
2567 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2568 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2569 GST_OBJECT_UNLOCK (overlay);
2570 /* Push the video frame */
2571 ret = gst_pad_push (overlay->srcpad, buffer);
2573 gchar *in_text, *otext;
2574 gsize in_size, osize;
2577 gst_buffer_map (overlay->text_buffer, &osize, NULL, GST_MAP_READ);
2581 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2582 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2583 * here on purpose, this is something that needs fixing upstream */
2584 if (!g_utf8_validate (in_text, in_size, NULL)) {
2585 const gchar *end = NULL;
2587 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2588 in_text = g_strndup (in_text, in_size);
2589 while (!g_utf8_validate (in_text, in_size, &end) && end)
2590 *((gchar *) end) = '*';
2593 /* Get the string */
2594 if (overlay->have_pango_markup) {
2595 text = g_strndup (in_text, in_size);
2597 text = g_markup_escape_text (in_text, in_size);
2600 if (text != NULL && *text != '\0') {
2601 gint text_len = strlen (text);
2603 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2604 text[text_len - 1] == '\r')) {
2607 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2608 gst_base_text_overlay_render_text (overlay, text, text_len);
2610 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2611 gst_base_text_overlay_render_text (overlay, " ", 1);
2613 gst_buffer_unmap (overlay->text_buffer, otext, osize);
2615 if (in_text != otext)
2618 GST_OBJECT_UNLOCK (overlay);
2619 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2621 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2622 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2627 GST_OBJECT_LOCK (overlay);
2628 gst_base_text_overlay_pop_text (overlay);
2629 GST_OBJECT_UNLOCK (overlay);
2632 gboolean wait_for_text_buf = TRUE;
2634 if (overlay->text_eos)
2635 wait_for_text_buf = FALSE;
2637 if (!overlay->wait_text)
2638 wait_for_text_buf = FALSE;
2640 /* Text pad linked, but no text buffer available - what now? */
2641 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2642 GstClockTime text_start_running_time, text_position_running_time;
2643 GstClockTime vid_running_time;
2646 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2647 GST_BUFFER_TIMESTAMP (buffer));
2648 text_start_running_time =
2649 gst_segment_to_running_time (&overlay->text_segment,
2650 GST_FORMAT_TIME, overlay->text_segment.start);
2651 text_position_running_time =
2652 gst_segment_to_running_time (&overlay->text_segment,
2653 GST_FORMAT_TIME, overlay->text_segment.position);
2655 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2656 vid_running_time < text_start_running_time) ||
2657 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2658 vid_running_time < text_position_running_time)) {
2659 wait_for_text_buf = FALSE;
2663 if (wait_for_text_buf) {
2664 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2665 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2666 GST_DEBUG_OBJECT (overlay, "resuming");
2667 GST_OBJECT_UNLOCK (overlay);
2668 goto wait_for_text_buf;
2670 GST_OBJECT_UNLOCK (overlay);
2671 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2672 ret = gst_pad_push (overlay->srcpad, buffer);
2679 /* Update position */
2680 overlay->segment.position = clip_start;
2686 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2687 gst_buffer_unref (buffer);
2693 GST_OBJECT_UNLOCK (overlay);
2694 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2695 gst_buffer_unref (buffer);
2696 return GST_FLOW_WRONG_STATE;
2700 GST_OBJECT_UNLOCK (overlay);
2701 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2702 gst_buffer_unref (buffer);
2703 return GST_FLOW_UNEXPECTED;
2707 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2708 gst_buffer_unref (buffer);
2713 static GstStateChangeReturn
2714 gst_base_text_overlay_change_state (GstElement * element,
2715 GstStateChange transition)
2717 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2718 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2720 switch (transition) {
2721 case GST_STATE_CHANGE_PAUSED_TO_READY:
2722 GST_OBJECT_LOCK (overlay);
2723 overlay->text_flushing = TRUE;
2724 overlay->video_flushing = TRUE;
2725 /* pop_text will broadcast on the GCond and thus also make the video
2726 * chain exit if it's waiting for a text buffer */
2727 gst_base_text_overlay_pop_text (overlay);
2728 GST_OBJECT_UNLOCK (overlay);
2734 ret = parent_class->change_state (element, transition);
2735 if (ret == GST_STATE_CHANGE_FAILURE)
2738 switch (transition) {
2739 case GST_STATE_CHANGE_READY_TO_PAUSED:
2740 GST_OBJECT_LOCK (overlay);
2741 overlay->text_flushing = FALSE;
2742 overlay->video_flushing = FALSE;
2743 overlay->video_eos = FALSE;
2744 overlay->text_eos = FALSE;
2745 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2746 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2747 GST_OBJECT_UNLOCK (overlay);
2757 plugin_init (GstPlugin * plugin)
2759 gst_controller_init (NULL, NULL);
2761 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2762 GST_TYPE_TEXT_OVERLAY) ||
2763 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2764 GST_TYPE_TIME_OVERLAY) ||
2765 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2766 GST_TYPE_CLOCK_OVERLAY) ||
2767 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2768 GST_TYPE_TEXT_RENDER)) {
2772 /*texttestsrc_plugin_init(module, plugin); */
2774 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2779 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2780 "pango", "Pango-based text rendering and overlay", plugin_init,
2781 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)