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)
68 * One can also feed arbitrary live text into the element:
70 * gst-launch fdsrc fd=0 ! text/plain ! txt. videotestsrc ! \
71 * textoverlay name=txt shaded-background=yes font-desc="Serif 40" wait-text=false ! \
73 * ]| This shows new text as entered on the terminal (stdin). This is not suited
74 * for subtitles as the test overlay is not timed. Subtitles should use
75 * timestamped formats. For the above use case one can also read the text from
76 * the application as set the #GstTextOverlay:text property.
81 /* FIXME: alloc segment as part of instance struct */
87 #include <gst/video/video.h>
89 #include "gsttextoverlay.h"
90 #include "gsttimeoverlay.h"
91 #include "gstclockoverlay.h"
92 #include "gsttextrender.h"
96 * - use proper strides and offset for I420
97 * - if text is wider than the video picture, it does not get
98 * clipped properly during blitting (if wrapping is disabled)
99 * - make 'shading_value' a property (or enum: light/normal/dark/verydark)?
102 GST_DEBUG_CATEGORY (pango_debug);
103 #define GST_CAT_DEFAULT pango_debug
105 #define DEFAULT_PROP_TEXT ""
106 #define DEFAULT_PROP_SHADING FALSE
107 #define DEFAULT_PROP_SHADOW TRUE
108 #define DEFAULT_PROP_VALIGNMENT GST_TEXT_OVERLAY_VALIGN_BASELINE
109 #define DEFAULT_PROP_HALIGNMENT GST_TEXT_OVERLAY_HALIGN_CENTER
110 #define DEFAULT_PROP_VALIGN "baseline"
111 #define DEFAULT_PROP_HALIGN "center"
112 #define DEFAULT_PROP_XPAD 25
113 #define DEFAULT_PROP_YPAD 25
114 #define DEFAULT_PROP_DELTAX 0
115 #define DEFAULT_PROP_DELTAY 0
116 #define DEFAULT_PROP_XPOS 0.5
117 #define DEFAULT_PROP_YPOS 0.5
118 #define DEFAULT_PROP_WRAP_MODE GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR
119 #define DEFAULT_PROP_FONT_DESC ""
120 #define DEFAULT_PROP_SILENT FALSE
121 #define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER
122 #define DEFAULT_PROP_WAIT_TEXT TRUE
123 #define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE
124 #define DEFAULT_PROP_VERTICAL_RENDER FALSE
125 #define DEFAULT_PROP_COLOR 0xffffffff
126 #define DEFAULT_PROP_OUTLINE_COLOR 0xff000000
128 /* make a property of me */
129 #define DEFAULT_SHADING_VALUE -80
131 #define MINIMUM_OUTLINE_OFFSET 1.0
132 #define DEFAULT_SCALE_BASIS 640
134 #define COMP_Y(ret, r, g, b) \
136 ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \
137 ret = CLAMP (ret, 0, 255); \
140 #define COMP_U(ret, r, g, b) \
142 ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \
143 ret = CLAMP (ret, 0, 255); \
146 #define COMP_V(ret, r, g, b) \
148 ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \
149 ret = CLAMP (ret, 0, 255); \
152 #define BLEND(ret, alpha, v0, v1) \
154 ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \
157 #define OVER(ret, alphaA, Ca, alphaB, Cb, alphaNew) \
160 _tmp = (Ca * alphaA + Cb * alphaB * (255 - alphaA) / 255) / alphaNew; \
161 ret = CLAMP (_tmp, 0, 255); \
164 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
165 # define CAIRO_ARGB_A 3
166 # define CAIRO_ARGB_R 2
167 # define CAIRO_ARGB_G 1
168 # define CAIRO_ARGB_B 0
170 # define CAIRO_ARGB_A 0
171 # define CAIRO_ARGB_R 1
172 # define CAIRO_ARGB_G 2
173 # define CAIRO_ARGB_B 3
181 PROP_VALIGN, /* deprecated */
182 PROP_HALIGN, /* deprecated */
196 PROP_AUTO_ADJUST_SIZE,
197 PROP_VERTICAL_RENDER,
204 static GstStaticPadTemplate src_template_factory =
205 GST_STATIC_PAD_TEMPLATE ("src",
208 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";"
209 GST_VIDEO_CAPS_RGBx ";"
210 GST_VIDEO_CAPS_xRGB ";"
211 GST_VIDEO_CAPS_xBGR ";"
212 GST_VIDEO_CAPS_RGBA ";"
213 GST_VIDEO_CAPS_BGRA ";"
214 GST_VIDEO_CAPS_ARGB ";"
215 GST_VIDEO_CAPS_ABGR ";"
216 GST_VIDEO_CAPS_YUV ("{AYUV, I420, YV12, UYVY, NV12, NV21}"))
219 static GstStaticPadTemplate video_sink_template_factory =
220 GST_STATIC_PAD_TEMPLATE ("video_sink",
223 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";"
224 GST_VIDEO_CAPS_RGBx ";"
225 GST_VIDEO_CAPS_xRGB ";"
226 GST_VIDEO_CAPS_xBGR ";"
227 GST_VIDEO_CAPS_RGBA ";"
228 GST_VIDEO_CAPS_BGRA ";"
229 GST_VIDEO_CAPS_ARGB ";"
230 GST_VIDEO_CAPS_ABGR ";"
231 GST_VIDEO_CAPS_YUV ("{AYUV, I420, YV12, UYVY, NV12, NV21}"))
234 static GstStaticPadTemplate text_sink_template_factory =
235 GST_STATIC_PAD_TEMPLATE ("text_sink",
238 GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
241 #define GST_TYPE_TEXT_OVERLAY_VALIGN (gst_text_overlay_valign_get_type())
243 gst_text_overlay_valign_get_type (void)
245 static GType text_overlay_valign_type = 0;
246 static const GEnumValue text_overlay_valign[] = {
247 {GST_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"},
248 {GST_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"},
249 {GST_TEXT_OVERLAY_VALIGN_TOP, "top", "top"},
250 {GST_TEXT_OVERLAY_VALIGN_POS, "position", "position"},
251 {GST_TEXT_OVERLAY_VALIGN_CENTER, "center", "center"},
255 if (!text_overlay_valign_type) {
256 text_overlay_valign_type =
257 g_enum_register_static ("GstTextOverlayVAlign", text_overlay_valign);
259 return text_overlay_valign_type;
262 #define GST_TYPE_TEXT_OVERLAY_HALIGN (gst_text_overlay_halign_get_type())
264 gst_text_overlay_halign_get_type (void)
266 static GType text_overlay_halign_type = 0;
267 static const GEnumValue text_overlay_halign[] = {
268 {GST_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"},
269 {GST_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"},
270 {GST_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"},
271 {GST_TEXT_OVERLAY_HALIGN_POS, "position", "position"},
275 if (!text_overlay_halign_type) {
276 text_overlay_halign_type =
277 g_enum_register_static ("GstTextOverlayHAlign", text_overlay_halign);
279 return text_overlay_halign_type;
283 #define GST_TYPE_TEXT_OVERLAY_WRAP_MODE (gst_text_overlay_wrap_mode_get_type())
285 gst_text_overlay_wrap_mode_get_type (void)
287 static GType text_overlay_wrap_mode_type = 0;
288 static const GEnumValue text_overlay_wrap_mode[] = {
289 {GST_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"},
290 {GST_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"},
291 {GST_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"},
292 {GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"},
296 if (!text_overlay_wrap_mode_type) {
297 text_overlay_wrap_mode_type =
298 g_enum_register_static ("GstTextOverlayWrapMode",
299 text_overlay_wrap_mode);
301 return text_overlay_wrap_mode_type;
304 #define GST_TYPE_TEXT_OVERLAY_LINE_ALIGN (gst_text_overlay_line_align_get_type())
306 gst_text_overlay_line_align_get_type (void)
308 static GType text_overlay_line_align_type = 0;
309 static const GEnumValue text_overlay_line_align[] = {
310 {GST_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"},
311 {GST_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"},
312 {GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"},
316 if (!text_overlay_line_align_type) {
317 text_overlay_line_align_type =
318 g_enum_register_static ("GstTextOverlayLineAlign",
319 text_overlay_line_align);
321 return text_overlay_line_align_type;
324 #define GST_TEXT_OVERLAY_GET_COND(ov) (((GstTextOverlay *)ov)->cond)
325 #define GST_TEXT_OVERLAY_WAIT(ov) (g_cond_wait (GST_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov)))
326 #define GST_TEXT_OVERLAY_SIGNAL(ov) (g_cond_signal (GST_TEXT_OVERLAY_GET_COND (ov)))
327 #define GST_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_TEXT_OVERLAY_GET_COND (ov)))
329 static GstStateChangeReturn gst_text_overlay_change_state (GstElement * element,
330 GstStateChange transition);
332 static GstCaps *gst_text_overlay_getcaps (GstPad * pad);
333 static gboolean gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps);
334 static gboolean gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps);
335 static gboolean gst_text_overlay_src_event (GstPad * pad, GstEvent * event);
336 static gboolean gst_text_overlay_src_query (GstPad * pad, GstQuery * query);
338 static gboolean gst_text_overlay_video_event (GstPad * pad, GstEvent * event);
339 static GstFlowReturn gst_text_overlay_video_chain (GstPad * pad,
341 static GstFlowReturn gst_text_overlay_video_bufferalloc (GstPad * pad,
342 guint64 offset, guint size, GstCaps * caps, GstBuffer ** buffer);
344 static gboolean gst_text_overlay_text_event (GstPad * pad, GstEvent * event);
345 static GstFlowReturn gst_text_overlay_text_chain (GstPad * pad,
347 static GstPadLinkReturn gst_text_overlay_text_pad_link (GstPad * pad,
349 static void gst_text_overlay_text_pad_unlink (GstPad * pad);
350 static void gst_text_overlay_pop_text (GstTextOverlay * overlay);
351 static void gst_text_overlay_update_render_mode (GstTextOverlay * overlay);
353 static void gst_text_overlay_finalize (GObject * object);
354 static void gst_text_overlay_set_property (GObject * object, guint prop_id,
355 const GValue * value, GParamSpec * pspec);
356 static void gst_text_overlay_get_property (GObject * object, guint prop_id,
357 GValue * value, GParamSpec * pspec);
358 static void gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay *
359 overlay, PangoFontDescription * desc);
361 GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement,
365 gst_text_overlay_base_init (gpointer g_class)
367 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
368 GstTextOverlayClass *klass = GST_TEXT_OVERLAY_CLASS (g_class);
369 PangoFontMap *fontmap;
371 gst_element_class_add_static_pad_template (element_class,
372 &src_template_factory);
373 gst_element_class_add_static_pad_template (element_class,
374 &video_sink_template_factory);
377 if (!GST_IS_TIME_OVERLAY_CLASS (g_class) &&
378 !GST_IS_CLOCK_OVERLAY_CLASS (g_class)) {
379 gst_element_class_add_static_pad_template (element_class,
380 &text_sink_template_factory);
383 gst_element_class_set_details_simple (element_class, "Text overlay",
384 "Filter/Editor/Video",
385 "Adds text strings on top of a video buffer",
386 "David Schleef <ds@schleef.org>, " "Zeeshan Ali <zeeshan.ali@nokia.com>");
388 /* Only lock for the subclasses here, the base class
389 * doesn't have this mutex yet and it's not necessary
391 if (klass->pango_lock)
392 g_mutex_lock (klass->pango_lock);
393 fontmap = pango_cairo_font_map_get_default ();
394 klass->pango_context =
395 pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
396 if (klass->pango_lock)
397 g_mutex_unlock (klass->pango_lock);
401 gst_text_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame)
403 return g_strdup (overlay->default_text);
407 gst_text_overlay_class_init (GstTextOverlayClass * klass)
409 GObjectClass *gobject_class;
410 GstElementClass *gstelement_class;
412 gobject_class = (GObjectClass *) klass;
413 gstelement_class = (GstElementClass *) klass;
415 gobject_class->finalize = gst_text_overlay_finalize;
416 gobject_class->set_property = gst_text_overlay_set_property;
417 gobject_class->get_property = gst_text_overlay_get_property;
419 gstelement_class->change_state =
420 GST_DEBUG_FUNCPTR (gst_text_overlay_change_state);
422 klass->pango_lock = g_mutex_new ();
424 klass->get_text = gst_text_overlay_get_text;
426 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
427 g_param_spec_string ("text", "text",
428 "Text to be display.", DEFAULT_PROP_TEXT,
429 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
430 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
431 g_param_spec_boolean ("shaded-background", "shaded background",
432 "Whether to shade the background under the text area",
433 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
435 * GstTextOverlay:shadow
437 * Whether to display a shadow of each letter under the text.
441 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADOW,
442 g_param_spec_boolean ("shadow", "create shadow of text",
443 "Whether to create a shadow of the letters under the text",
444 DEFAULT_PROP_SHADOW, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
445 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
446 g_param_spec_enum ("valignment", "vertical alignment",
447 "Vertical alignment of the text", GST_TYPE_TEXT_OVERLAY_VALIGN,
448 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
449 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
450 g_param_spec_enum ("halignment", "horizontal alignment",
451 "Horizontal alignment of the text", GST_TYPE_TEXT_OVERLAY_HALIGN,
452 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
453 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
454 g_param_spec_string ("valign", "vertical alignment",
455 "Vertical alignment of the text (deprecated; use valignment)",
456 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
457 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
458 g_param_spec_string ("halign", "horizontal alignment",
459 "Horizontal alignment of the text (deprecated; use halignment)",
460 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
461 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
462 g_param_spec_int ("xpad", "horizontal paddding",
463 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
464 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
465 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
466 g_param_spec_int ("ypad", "vertical padding",
467 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
468 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
469 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
470 g_param_spec_int ("deltax", "X position modifier",
471 "Shift X position to the left or to the right. Unit is pixels.",
472 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
473 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
474 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
475 g_param_spec_int ("deltay", "Y position modifier",
476 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
477 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
479 * GstTextOverlay:xpos
481 * Horizontal position of the rendered text when using positioned alignment.
485 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
486 g_param_spec_double ("xpos", "horizontal position",
487 "Horizontal position when using position alignment", 0, 1.0,
489 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
491 * GstTextOverlay:ypos
493 * Vertical position of the rendered text when using positioned alignment.
497 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
498 g_param_spec_double ("ypos", "vertical position",
499 "Vertical position when using position alignment", 0, 1.0,
501 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
502 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
503 g_param_spec_enum ("wrap-mode", "wrap mode",
504 "Whether to wrap the text and if so how.",
505 GST_TYPE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
506 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
507 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
508 g_param_spec_string ("font-desc", "font description",
509 "Pango font description of font to be used for rendering. "
510 "See documentation of pango_font_description_from_string "
511 "for syntax.", DEFAULT_PROP_FONT_DESC,
512 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
514 * GstTextOverlay:color
516 * Color of the rendered text.
520 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
521 g_param_spec_uint ("color", "Color",
522 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
524 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
526 * GstTextOverlay:outline-color
528 * Color of the outline of the rendered text.
532 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
533 g_param_spec_uint ("outline-color", "Text Outline Color",
534 "Color to use for outline the text (big-endian ARGB).", 0,
535 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
536 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
539 * GstTextOverlay:line-alignment
541 * Alignment of text lines relative to each other (for multi-line text)
545 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
546 g_param_spec_enum ("line-alignment", "line alignment",
547 "Alignment of text lines relative to each other.",
548 GST_TYPE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
549 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
551 * GstTextOverlay:silent
553 * If set, no text is rendered. Useful to switch off text rendering
554 * temporarily without removing the textoverlay element from the pipeline.
558 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
559 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
560 g_param_spec_boolean ("silent", "silent",
561 "Whether to render the text string",
563 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
565 * GstTextOverlay:wait-text
567 * If set, the video will block until a subtitle is received on the text pad.
568 * If video and subtitles are sent in sync, like from the same demuxer, this
569 * property should be set.
573 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
574 g_param_spec_boolean ("wait-text", "Wait Text",
575 "Whether to wait for subtitles",
576 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
578 g_object_class_install_property (G_OBJECT_CLASS (klass),
579 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
580 "Automatically adjust font size to screen-size.",
581 DEFAULT_PROP_AUTO_ADJUST_SIZE,
582 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
584 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
585 g_param_spec_boolean ("vertical-render", "vertical render",
586 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
587 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
591 gst_text_overlay_finalize (GObject * object)
593 GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
595 g_free (overlay->default_text);
597 if (overlay->text_image) {
598 g_free (overlay->text_image);
599 overlay->text_image = NULL;
602 if (overlay->layout) {
603 g_object_unref (overlay->layout);
604 overlay->layout = NULL;
607 if (overlay->text_buffer) {
608 gst_buffer_unref (overlay->text_buffer);
609 overlay->text_buffer = NULL;
613 g_cond_free (overlay->cond);
614 overlay->cond = NULL;
617 G_OBJECT_CLASS (parent_class)->finalize (object);
621 gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * 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_text_overlay_getcaps));
632 gst_pad_set_setcaps_function (overlay->video_sinkpad,
633 GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps));
634 gst_pad_set_event_function (overlay->video_sinkpad,
635 GST_DEBUG_FUNCPTR (gst_text_overlay_video_event));
636 gst_pad_set_chain_function (overlay->video_sinkpad,
637 GST_DEBUG_FUNCPTR (gst_text_overlay_video_chain));
638 gst_pad_set_bufferalloc_function (overlay->video_sinkpad,
639 GST_DEBUG_FUNCPTR (gst_text_overlay_video_bufferalloc));
640 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
642 if (!GST_IS_TIME_OVERLAY_CLASS (klass) && !GST_IS_CLOCK_OVERLAY_CLASS (klass)) {
644 template = gst_static_pad_template_get (&text_sink_template_factory);
645 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
646 gst_object_unref (template);
647 gst_pad_set_setcaps_function (overlay->text_sinkpad,
648 GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps_txt));
649 gst_pad_set_event_function (overlay->text_sinkpad,
650 GST_DEBUG_FUNCPTR (gst_text_overlay_text_event));
651 gst_pad_set_chain_function (overlay->text_sinkpad,
652 GST_DEBUG_FUNCPTR (gst_text_overlay_text_chain));
653 gst_pad_set_link_function (overlay->text_sinkpad,
654 GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_link));
655 gst_pad_set_unlink_function (overlay->text_sinkpad,
656 GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_unlink));
657 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
661 template = gst_static_pad_template_get (&src_template_factory);
662 overlay->srcpad = gst_pad_new_from_template (template, "src");
663 gst_object_unref (template);
664 gst_pad_set_getcaps_function (overlay->srcpad,
665 GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
666 gst_pad_set_event_function (overlay->srcpad,
667 GST_DEBUG_FUNCPTR (gst_text_overlay_src_event));
668 gst_pad_set_query_function (overlay->srcpad,
669 GST_DEBUG_FUNCPTR (gst_text_overlay_src_query));
670 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
672 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
673 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
675 pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context);
677 pango_context_get_font_description (GST_TEXT_OVERLAY_GET_CLASS
678 (overlay)->pango_context);
679 gst_text_overlay_adjust_values_with_fontdesc (overlay, desc);
681 overlay->color = DEFAULT_PROP_COLOR;
682 overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
683 overlay->halign = DEFAULT_PROP_HALIGNMENT;
684 overlay->valign = DEFAULT_PROP_VALIGNMENT;
685 overlay->xpad = DEFAULT_PROP_XPAD;
686 overlay->ypad = DEFAULT_PROP_YPAD;
687 overlay->deltax = DEFAULT_PROP_DELTAX;
688 overlay->deltay = DEFAULT_PROP_DELTAY;
689 overlay->xpos = DEFAULT_PROP_XPOS;
690 overlay->ypos = DEFAULT_PROP_YPOS;
692 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
694 overlay->want_shading = DEFAULT_PROP_SHADING;
695 overlay->want_shadow = DEFAULT_PROP_SHADOW;
696 overlay->shading_value = DEFAULT_SHADING_VALUE;
697 overlay->silent = DEFAULT_PROP_SILENT;
698 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
699 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
701 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
702 overlay->need_render = TRUE;
703 overlay->text_image = NULL;
704 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
705 gst_text_overlay_update_render_mode (overlay);
710 overlay->text_buffer = NULL;
711 overlay->text_linked = FALSE;
712 overlay->cond = g_cond_new ();
713 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
714 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
718 gst_text_overlay_update_wrap_mode (GstTextOverlay * overlay)
720 if (overlay->wrap_mode == GST_TEXT_OVERLAY_WRAP_MODE_NONE) {
721 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
722 pango_layout_set_width (overlay->layout, -1);
726 if (overlay->auto_adjust_size) {
727 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
728 if (overlay->use_vertical_render) {
729 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
733 (overlay->use_vertical_render ? overlay->height : overlay->width) *
737 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
738 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
739 pango_layout_set_width (overlay->layout, width);
740 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
745 gst_text_overlay_update_render_mode (GstTextOverlay * overlay)
747 PangoMatrix matrix = PANGO_MATRIX_INIT;
748 PangoContext *context = pango_layout_get_context (overlay->layout);
750 if (overlay->use_vertical_render) {
751 pango_matrix_rotate (&matrix, -90);
752 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
753 pango_context_set_matrix (context, &matrix);
754 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
756 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
757 pango_context_set_matrix (context, &matrix);
758 pango_layout_set_alignment (overlay->layout, overlay->line_align);
763 gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps)
765 GstTextOverlay *overlay;
766 GstStructure *structure;
768 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
769 if (G_UNLIKELY (!overlay))
772 structure = gst_caps_get_structure (caps, 0);
773 overlay->have_pango_markup =
774 gst_structure_has_name (structure, "text/x-pango-markup");
776 gst_object_unref (overlay);
781 /* FIXME: upstream nego (e.g. when the video window is resized) */
784 gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps)
786 GstTextOverlay *overlay;
787 GstStructure *structure;
788 gboolean ret = FALSE;
791 if (!GST_PAD_IS_SINK (pad))
794 g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
796 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
797 if (G_UNLIKELY (!overlay))
802 structure = gst_caps_get_structure (caps, 0);
803 fps = gst_structure_get_value (structure, "framerate");
806 && gst_video_format_parse_caps (caps, &overlay->format, &overlay->width,
808 ret = gst_pad_set_caps (overlay->srcpad, caps);
811 overlay->fps_n = gst_value_get_fraction_numerator (fps);
812 overlay->fps_d = gst_value_get_fraction_denominator (fps);
815 GST_OBJECT_LOCK (overlay);
816 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
817 gst_text_overlay_update_wrap_mode (overlay);
818 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
819 GST_OBJECT_UNLOCK (overlay);
822 gst_object_unref (overlay);
828 gst_text_overlay_set_property (GObject * object, guint prop_id,
829 const GValue * value, GParamSpec * pspec)
831 GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
833 GST_OBJECT_LOCK (overlay);
836 g_free (overlay->default_text);
837 overlay->default_text = g_value_dup_string (value);
838 overlay->need_render = TRUE;
841 overlay->want_shading = g_value_get_boolean (value);
844 overlay->want_shadow = g_value_get_boolean (value);
847 overlay->xpad = g_value_get_int (value);
850 overlay->ypad = g_value_get_int (value);
853 overlay->deltax = g_value_get_int (value);
856 overlay->deltay = g_value_get_int (value);
859 overlay->xpos = g_value_get_double (value);
862 overlay->ypos = g_value_get_double (value);
865 const gchar *s = g_value_get_string (value);
867 if (s && g_ascii_strcasecmp (s, "left") == 0)
868 overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
869 else if (s && g_ascii_strcasecmp (s, "center") == 0)
870 overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
871 else if (s && g_ascii_strcasecmp (s, "right") == 0)
872 overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
874 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
879 const gchar *s = g_value_get_string (value);
881 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
882 overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
883 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
884 overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM;
885 else if (s && g_ascii_strcasecmp (s, "top") == 0)
886 overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
888 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
892 case PROP_VALIGNMENT:
893 overlay->valign = g_value_get_enum (value);
895 case PROP_HALIGNMENT:
896 overlay->halign = g_value_get_enum (value);
899 overlay->wrap_mode = g_value_get_enum (value);
900 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
901 gst_text_overlay_update_wrap_mode (overlay);
902 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
906 PangoFontDescription *desc;
907 const gchar *fontdesc_str;
909 fontdesc_str = g_value_get_string (value);
910 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
911 desc = pango_font_description_from_string (fontdesc_str);
913 GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
914 pango_layout_set_font_description (overlay->layout, desc);
915 gst_text_overlay_adjust_values_with_fontdesc (overlay, desc);
916 pango_font_description_free (desc);
918 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
921 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
925 overlay->color = g_value_get_uint (value);
927 case PROP_OUTLINE_COLOR:
928 overlay->outline_color = g_value_get_uint (value);
931 overlay->silent = g_value_get_boolean (value);
933 case PROP_LINE_ALIGNMENT:
934 overlay->line_align = g_value_get_enum (value);
935 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
936 pango_layout_set_alignment (overlay->layout,
937 (PangoAlignment) overlay->line_align);
938 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
941 overlay->wait_text = g_value_get_boolean (value);
943 case PROP_AUTO_ADJUST_SIZE:
944 overlay->auto_adjust_size = g_value_get_boolean (value);
945 overlay->need_render = TRUE;
947 case PROP_VERTICAL_RENDER:
948 overlay->use_vertical_render = g_value_get_boolean (value);
949 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
950 gst_text_overlay_update_render_mode (overlay);
951 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
952 overlay->need_render = TRUE;
955 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
959 overlay->need_render = TRUE;
960 GST_OBJECT_UNLOCK (overlay);
964 gst_text_overlay_get_property (GObject * object, guint prop_id,
965 GValue * value, GParamSpec * pspec)
967 GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
969 GST_OBJECT_LOCK (overlay);
972 g_value_set_string (value, overlay->default_text);
975 g_value_set_boolean (value, overlay->want_shading);
978 g_value_set_boolean (value, overlay->want_shadow);
981 g_value_set_int (value, overlay->xpad);
984 g_value_set_int (value, overlay->ypad);
987 g_value_set_int (value, overlay->deltax);
990 g_value_set_int (value, overlay->deltay);
993 g_value_set_double (value, overlay->xpos);
996 g_value_set_double (value, overlay->ypos);
998 case PROP_VALIGNMENT:
999 g_value_set_enum (value, overlay->valign);
1001 case PROP_HALIGNMENT:
1002 g_value_set_enum (value, overlay->halign);
1004 case PROP_WRAP_MODE:
1005 g_value_set_enum (value, overlay->wrap_mode);
1008 g_value_set_boolean (value, overlay->silent);
1010 case PROP_LINE_ALIGNMENT:
1011 g_value_set_enum (value, overlay->line_align);
1013 case PROP_WAIT_TEXT:
1014 g_value_set_boolean (value, overlay->wait_text);
1016 case PROP_AUTO_ADJUST_SIZE:
1017 g_value_set_boolean (value, overlay->auto_adjust_size);
1019 case PROP_VERTICAL_RENDER:
1020 g_value_set_boolean (value, overlay->use_vertical_render);
1023 g_value_set_uint (value, overlay->color);
1025 case PROP_OUTLINE_COLOR:
1026 g_value_set_uint (value, overlay->outline_color);
1029 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1033 overlay->need_render = TRUE;
1034 GST_OBJECT_UNLOCK (overlay);
1038 gst_text_overlay_src_query (GstPad * pad, GstQuery * query)
1040 gboolean ret = FALSE;
1041 GstTextOverlay *overlay = NULL;
1043 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
1044 if (G_UNLIKELY (!overlay))
1047 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1049 gst_object_unref (overlay);
1055 gst_text_overlay_src_event (GstPad * pad, GstEvent * event)
1057 gboolean ret = FALSE;
1058 GstTextOverlay *overlay = NULL;
1060 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
1061 if (G_UNLIKELY (!overlay)) {
1062 gst_event_unref (event);
1066 switch (GST_EVENT_TYPE (event)) {
1067 case GST_EVENT_SEEK:{
1070 /* We don't handle seek if we have not text pad */
1071 if (!overlay->text_linked) {
1072 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1073 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1077 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1079 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1081 /* Flush downstream, only for flushing seek */
1082 if (flags & GST_SEEK_FLAG_FLUSH)
1083 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1085 /* Mark ourself as flushing, unblock chains */
1086 GST_OBJECT_LOCK (overlay);
1087 overlay->video_flushing = TRUE;
1088 overlay->text_flushing = TRUE;
1089 gst_text_overlay_pop_text (overlay);
1090 GST_OBJECT_UNLOCK (overlay);
1092 /* Seek on each sink pad */
1093 gst_event_ref (event);
1094 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1096 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1098 gst_event_unref (event);
1103 if (overlay->text_linked) {
1104 gst_event_ref (event);
1105 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1106 gst_pad_push_event (overlay->text_sinkpad, event);
1108 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1114 gst_object_unref (overlay);
1120 gst_text_overlay_getcaps (GstPad * pad)
1122 GstTextOverlay *overlay;
1126 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
1127 if (G_UNLIKELY (!overlay))
1128 return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1130 if (pad == overlay->srcpad)
1131 otherpad = overlay->video_sinkpad;
1133 otherpad = overlay->srcpad;
1135 /* we can do what the peer can */
1136 caps = gst_pad_peer_get_caps (otherpad);
1139 const GstCaps *templ;
1141 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1143 /* filtered against our padtemplate */
1144 templ = gst_pad_get_pad_template_caps (otherpad);
1145 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1146 temp = gst_caps_intersect (caps, templ);
1147 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1148 gst_caps_unref (caps);
1149 /* this is what we can do */
1152 /* no peer, our padtemplate is enough then */
1153 caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1156 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1158 gst_object_unref (overlay);
1164 gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * overlay,
1165 PangoFontDescription * desc)
1167 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1168 overlay->shadow_offset = (double) (font_size) / 13.0;
1169 overlay->outline_offset = (double) (font_size) / 15.0;
1170 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1171 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1174 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1175 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1176 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1177 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1181 gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest, gint xpos,
1182 gint ypos, guchar * text_image, guint dest_stride)
1189 gint width = overlay->image_width;
1190 gint height = overlay->image_height;
1196 if (xpos + width > overlay->width) {
1197 width = overlay->width - xpos;
1200 if (ypos + height > overlay->height) {
1201 height = overlay->height - ypos;
1204 dest += (ypos / 1) * dest_stride;
1206 for (i = 0; i < height; i++) {
1207 pimage = text_image + 4 * (i * overlay->image_width);
1208 py = dest + i * dest_stride + xpos;
1209 for (j = 0; j < width; j++) {
1210 b = pimage[CAIRO_ARGB_B];
1211 g = pimage[CAIRO_ARGB_G];
1212 r = pimage[CAIRO_ARGB_R];
1213 a = pimage[CAIRO_ARGB_A];
1214 CAIRO_UNPREMULTIPLY (a, r, g, b);
1221 COMP_Y (y, r, g, b);
1223 BLEND (*py++, a, y, x);
1229 gst_text_overlay_blit_sub2x2cbcr (GstTextOverlay * overlay,
1230 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1231 guint destcb_stride, guint destcr_stride, guint pix_stride)
1236 gushort r1, g1, b1, a1;
1237 guchar *pimage1, *pimage2;
1239 gint width = overlay->image_width - 2;
1240 gint height = overlay->image_height - 2;
1248 if (xpos + width > overlay->width) {
1249 width = overlay->width - xpos;
1252 if (ypos + height > overlay->height) {
1253 height = overlay->height - ypos;
1256 destcb += (ypos / 2) * destcb_stride;
1257 destcr += (ypos / 2) * destcr_stride;
1259 for (i = 0; i < height; i += 2) {
1260 pimage1 = text_image + 4 * (i * overlay->image_width);
1261 pimage2 = pimage1 + 4 * overlay->image_width;
1262 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1263 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1264 for (j = 0; j < width; j += 2) {
1265 b = pimage1[CAIRO_ARGB_B];
1266 g = pimage1[CAIRO_ARGB_G];
1267 r = pimage1[CAIRO_ARGB_R];
1268 a = pimage1[CAIRO_ARGB_A];
1269 CAIRO_UNPREMULTIPLY (a, r, g, b);
1272 b1 = pimage1[CAIRO_ARGB_B];
1273 g1 = pimage1[CAIRO_ARGB_G];
1274 r1 = pimage1[CAIRO_ARGB_R];
1275 a1 = pimage1[CAIRO_ARGB_A];
1276 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1283 b1 = pimage2[CAIRO_ARGB_B];
1284 g1 = pimage2[CAIRO_ARGB_G];
1285 r1 = pimage2[CAIRO_ARGB_R];
1286 a1 = pimage2[CAIRO_ARGB_A];
1287 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1294 /* + 2 for rounding */
1295 b1 = pimage2[CAIRO_ARGB_B];
1296 g1 = pimage2[CAIRO_ARGB_G];
1297 r1 = pimage2[CAIRO_ARGB_R];
1298 a1 = pimage2[CAIRO_ARGB_A];
1299 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1316 COMP_U (cb, r, g, b);
1317 COMP_V (cr, r, g, b);
1320 BLEND (*pcb, a, cb, x);
1322 BLEND (*pcr, a, cr, x);
1331 gst_text_overlay_render_pangocairo (GstTextOverlay * overlay,
1332 const gchar * string, gint textlen)
1335 cairo_surface_t *surface;
1336 PangoRectangle ink_rect, logical_rect;
1337 cairo_matrix_t cairo_matrix;
1339 double scalef = 1.0;
1342 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1344 if (overlay->auto_adjust_size) {
1345 /* 640 pixel is default */
1346 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1348 pango_layout_set_width (overlay->layout, -1);
1349 /* set text on pango layout */
1350 pango_layout_set_markup (overlay->layout, string, textlen);
1352 /* get subtitle image size */
1353 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1355 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1357 if (width + overlay->deltax >
1358 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1360 * subtitle image width is larger then overlay width
1361 * so rearrange overlay wrap mode.
1363 gst_text_overlay_update_wrap_mode (overlay);
1364 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1365 width = overlay->width;
1369 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1370 if (height > overlay->height) {
1371 height = overlay->height;
1373 if (overlay->use_vertical_render) {
1374 PangoRectangle rect;
1375 PangoContext *context;
1376 PangoMatrix matrix = PANGO_MATRIX_INIT;
1379 context = pango_layout_get_context (overlay->layout);
1381 pango_matrix_rotate (&matrix, -90);
1383 rect.x = rect.y = 0;
1385 rect.height = height;
1386 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1387 matrix.x0 = -rect.x;
1388 matrix.y0 = -rect.y;
1390 pango_context_set_matrix (context, &matrix);
1392 cairo_matrix.xx = matrix.xx;
1393 cairo_matrix.yx = matrix.yx;
1394 cairo_matrix.xy = matrix.xy;
1395 cairo_matrix.yy = matrix.yy;
1396 cairo_matrix.x0 = matrix.x0;
1397 cairo_matrix.y0 = matrix.y0;
1398 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1404 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1407 /* reallocate surface */
1408 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1410 surface = cairo_image_surface_create_for_data (overlay->text_image,
1411 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1412 cr = cairo_create (surface);
1415 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1418 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1420 if (overlay->want_shading)
1421 cairo_paint_with_alpha (cr, overlay->shading_value);
1423 /* apply transformations */
1424 cairo_set_matrix (cr, &cairo_matrix);
1426 /* FIXME: We use show_layout everywhere except for the surface
1427 * because it's really faster and internally does all kinds of
1428 * caching. Unfortunately we have to paint to a cairo path for
1429 * the outline and this is slow. Once Pango supports user fonts
1430 * we should use them, see
1431 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1433 * Idea would the be, to create a cairo user font that
1434 * does shadow, outline, text painting in the
1435 * render_glyph function.
1438 /* draw shadow text */
1439 if (overlay->want_shadow) {
1441 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1442 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1443 pango_cairo_show_layout (cr, overlay->layout);
1447 a = (overlay->outline_color >> 24) & 0xff;
1448 r = (overlay->outline_color >> 16) & 0xff;
1449 g = (overlay->outline_color >> 8) & 0xff;
1450 b = (overlay->outline_color >> 0) & 0xff;
1452 /* draw outline text */
1454 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1455 cairo_set_line_width (cr, overlay->outline_offset);
1456 pango_cairo_layout_path (cr, overlay->layout);
1460 a = (overlay->color >> 24) & 0xff;
1461 r = (overlay->color >> 16) & 0xff;
1462 g = (overlay->color >> 8) & 0xff;
1463 b = (overlay->color >> 0) & 0xff;
1467 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1468 pango_cairo_show_layout (cr, overlay->layout);
1472 cairo_surface_destroy (surface);
1473 overlay->image_width = width;
1474 overlay->image_height = height;
1475 overlay->baseline_y = ink_rect.y;
1477 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1484 gst_text_overlay_shade_planar_Y (GstTextOverlay * overlay, guchar * dest,
1485 gint x0, gint x1, gint y0, gint y1)
1487 gint i, j, dest_stride;
1489 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1492 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1493 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1495 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1496 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1498 for (i = y0; i < y1; ++i) {
1499 for (j = x0; j < x1; ++j) {
1500 gint y = dest[(i * dest_stride) + j] + overlay->shading_value;
1502 dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1508 gst_text_overlay_shade_packed_Y (GstTextOverlay * overlay, guchar * dest,
1509 gint x0, gint x1, gint y0, gint y1)
1512 guint dest_stride, pixel_stride, component_offset;
1514 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1516 pixel_stride = gst_video_format_get_pixel_stride (overlay->format, 0);
1518 gst_video_format_get_component_offset (overlay->format, 0, overlay->width,
1521 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1522 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1524 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1525 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1528 x0 = gst_video_format_get_component_width (overlay->format, 0, x0);
1530 x1 = gst_video_format_get_component_width (overlay->format, 0, x1);
1533 y0 = gst_video_format_get_component_height (overlay->format, 0, y0);
1535 y1 = gst_video_format_get_component_height (overlay->format, 0, y1);
1537 for (i = y0; i < y1; i++) {
1538 for (j = x0; j < x1; j++) {
1542 y_pos = (i * dest_stride) + j * pixel_stride + component_offset;
1543 y = dest[y_pos] + overlay->shading_value;
1545 dest[y_pos] = CLAMP (y, 0, 255);
1550 #define gst_text_overlay_shade_BGRx gst_text_overlay_shade_xRGB
1551 #define gst_text_overlay_shade_RGBx gst_text_overlay_shade_xRGB
1552 #define gst_text_overlay_shade_xBGR gst_text_overlay_shade_xRGB
1554 gst_text_overlay_shade_xRGB (GstTextOverlay * overlay, guchar * dest,
1555 gint x0, gint x1, gint y0, gint y1)
1559 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1560 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1562 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1563 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1565 for (i = y0; i < y1; i++) {
1566 for (j = x0; j < x1; j++) {
1569 y_pos = (i * 4 * overlay->width) + j * 4;
1570 for (k = 0; k < 4; k++) {
1571 y = dest[y_pos + k] + overlay->shading_value;
1572 dest[y_pos + k] = CLAMP (y, 0, 255);
1578 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1579 static inline void \
1580 gst_text_overlay_shade_##name (GstTextOverlay * overlay, guchar * dest, \
1581 gint x0, gint x1, gint y0, gint y1) \
1585 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1586 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1588 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1589 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1591 for (i = y0; i < y1; i++) {\
1592 for (j = x0; j < x1; j++) {\
1594 y_pos = (i * 4 * overlay->width) + j * 4;\
1595 for (k = OFFSET; k < 3+OFFSET; k++) {\
1596 y = dest[y_pos + k] + overlay->shading_value;\
1597 dest[y_pos + k] = CLAMP (y, 0, 255);\
1602 ARGB_SHADE_FUNCTION (ARGB, 1);
1603 ARGB_SHADE_FUNCTION (ABGR, 1);
1604 ARGB_SHADE_FUNCTION (RGBA, 0);
1605 ARGB_SHADE_FUNCTION (BGRA, 0);
1609 * - use proper strides and offset for I420
1610 * - don't draw over the edge of the picture (try a longer
1611 * text with a huge font size)
1615 gst_text_overlay_blit_NV12_NV21 (GstTextOverlay * overlay,
1616 guint8 * yuv_pixels, gint xpos, gint ypos)
1618 int y_stride, uv_stride;
1619 int u_offset, v_offset;
1622 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1623 * to a boundary of integer number of U/V pixels:
1625 xpos = GST_ROUND_UP_2 (xpos);
1626 ypos = GST_ROUND_UP_2 (ypos);
1629 h = overlay->height;
1631 y_stride = gst_video_format_get_row_stride (overlay->format, 0, w);
1632 uv_stride = gst_video_format_get_row_stride (overlay->format, 1, w);
1633 u_offset = gst_video_format_get_component_offset (overlay->format, 1, w, h);
1634 v_offset = gst_video_format_get_component_offset (overlay->format, 2, w, h);
1636 gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image,
1638 gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1639 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, uv_stride,
1644 gst_text_overlay_blit_I420_YV12 (GstTextOverlay * overlay,
1645 guint8 * yuv_pixels, gint xpos, gint ypos)
1647 int y_stride, u_stride, v_stride;
1648 int u_offset, v_offset;
1651 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1652 * to a boundary of integer number of U/V pixels:
1654 xpos = GST_ROUND_UP_2 (xpos);
1655 ypos = GST_ROUND_UP_2 (ypos);
1658 h = overlay->height;
1660 y_stride = gst_video_format_get_row_stride (overlay->format, 0, w);
1661 u_stride = gst_video_format_get_row_stride (overlay->format, 1, w);
1662 v_stride = gst_video_format_get_row_stride (overlay->format, 2, w);
1663 u_offset = gst_video_format_get_component_offset (overlay->format, 1, w, h);
1664 v_offset = gst_video_format_get_component_offset (overlay->format, 2, w, h);
1666 gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image,
1668 gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1669 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride,
1674 gst_text_overlay_blit_UYVY (GstTextOverlay * overlay,
1675 guint8 * yuv_pixels, gint xpos, gint ypos)
1682 guchar *pimage, *dest;
1684 /* because U/V is 2x horizontally subsampled, we need to round to a
1685 * boundary of integer number of U/V pixels in x dimension:
1687 xpos = GST_ROUND_UP_2 (xpos);
1689 w = overlay->image_width - 2;
1690 h = overlay->image_height - 2;
1696 if (xpos + w > overlay->width) {
1697 w = overlay->width - xpos;
1700 if (ypos + h > overlay->height) {
1701 h = overlay->height - ypos;
1704 for (i = 0; i < h; i++) {
1705 pimage = overlay->text_image + i * overlay->image_width * 4;
1706 dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1707 for (j = 0; j < w; j += 2) {
1708 b0 = pimage[CAIRO_ARGB_B];
1709 g0 = pimage[CAIRO_ARGB_G];
1710 r0 = pimage[CAIRO_ARGB_R];
1711 a0 = pimage[CAIRO_ARGB_A];
1712 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1715 b1 = pimage[CAIRO_ARGB_B];
1716 g1 = pimage[CAIRO_ARGB_G];
1717 r1 = pimage[CAIRO_ARGB_R];
1718 a1 = pimage[CAIRO_ARGB_A];
1719 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1729 COMP_Y (y0, r0, g0, b0);
1730 COMP_Y (y1, r1, g1, b1);
1740 COMP_U (u, r0, g0, b0);
1741 COMP_V (v, r0, g0, b0);
1743 BLEND (*dest, a0, u, *dest);
1745 BLEND (*dest, a0, y0, *dest);
1747 BLEND (*dest, a0, v, *dest);
1749 BLEND (*dest, a0, y1, *dest);
1756 gst_text_overlay_blit_AYUV (GstTextOverlay * overlay,
1757 guint8 * rgb_pixels, gint xpos, gint ypos)
1763 guchar *pimage, *dest;
1765 w = overlay->image_width;
1766 h = overlay->image_height;
1772 if (xpos + w > overlay->width) {
1773 w = overlay->width - xpos;
1776 if (ypos + h > overlay->height) {
1777 h = overlay->height - ypos;
1780 for (i = 0; i < h; i++) {
1781 pimage = overlay->text_image + i * overlay->image_width * 4;
1782 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1783 for (j = 0; j < w; j++) {
1784 a = pimage[CAIRO_ARGB_A];
1785 b = pimage[CAIRO_ARGB_B];
1786 g = pimage[CAIRO_ARGB_G];
1787 r = pimage[CAIRO_ARGB_R];
1789 CAIRO_UNPREMULTIPLY (a, r, g, b);
1791 // convert background to yuv
1792 COMP_Y (y, r, g, b);
1793 COMP_U (u, r, g, b);
1794 COMP_V (v, r, g, b);
1796 // preform text "OVER" background alpha compositing
1797 a1 = a + (dest[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1798 OVER (dest[1], a, y, dest[0], dest[1], a1);
1799 OVER (dest[2], a, u, dest[0], dest[2], a1);
1800 OVER (dest[3], a, v, dest[0], dest[3], a1);
1801 dest[0] = a1 - 1; // remove the temporary 1 we added
1809 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1810 static inline void \
1811 gst_text_overlay_blit_##name (GstTextOverlay * overlay, \
1812 guint8 * rgb_pixels, gint xpos, gint ypos) \
1817 guchar *pimage, *dest; \
1819 w = overlay->image_width; \
1820 h = overlay->image_height; \
1826 if (xpos + w > overlay->width) { \
1827 w = overlay->width - xpos; \
1830 if (ypos + h > overlay->height) { \
1831 h = overlay->height - ypos; \
1834 for (i = 0; i < h; i++) { \
1835 pimage = overlay->text_image + i * overlay->image_width * 4; \
1836 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1837 for (j = 0; j < w; j++) { \
1838 a = pimage[CAIRO_ARGB_A]; \
1839 b = pimage[CAIRO_ARGB_B]; \
1840 g = pimage[CAIRO_ARGB_G]; \
1841 r = pimage[CAIRO_ARGB_R]; \
1842 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1843 b = (b*a + dest[B] * (255-a)) / 255; \
1844 g = (g*a + dest[G] * (255-a)) / 255; \
1845 r = (r*a + dest[R] * (255-a)) / 255; \
1855 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1856 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1857 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1858 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1860 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1861 static inline void \
1862 gst_text_overlay_blit_##name (GstTextOverlay * overlay, \
1863 guint8 * rgb_pixels, gint xpos, gint ypos) \
1865 int a, r, g, b, a1; \
1868 guchar *pimage, *dest; \
1870 w = overlay->image_width; \
1871 h = overlay->image_height; \
1877 if (xpos + w > overlay->width) { \
1878 w = overlay->width - xpos; \
1881 if (ypos + h > overlay->height) { \
1882 h = overlay->height - ypos; \
1885 for (i = 0; i < h; i++) { \
1886 pimage = overlay->text_image + i * overlay->image_width * 4; \
1887 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1888 for (j = 0; j < w; j++) { \
1889 a = pimage[CAIRO_ARGB_A]; \
1890 b = pimage[CAIRO_ARGB_B]; \
1891 g = pimage[CAIRO_ARGB_G]; \
1892 r = pimage[CAIRO_ARGB_R]; \
1893 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1894 a1 = a + (dest[A] * (255 - a)) / 255 + 1; \
1895 OVER (dest[R], a, r, dest[0], dest[R], a1); \
1896 OVER (dest[G], a, g, dest[0], dest[G], a1); \
1897 OVER (dest[B], a, b, dest[0], dest[B], a1); \
1904 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1905 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1906 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1907 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1910 gst_text_overlay_render_text (GstTextOverlay * overlay,
1911 const gchar * text, gint textlen)
1915 if (!overlay->need_render) {
1916 GST_DEBUG ("Using previously rendered text.");
1920 /* -1 is the whole string */
1921 if (text != NULL && textlen < 0) {
1922 textlen = strlen (text);
1926 string = g_strndup (text, textlen);
1927 } else { /* empty string */
1928 string = g_strdup (" ");
1930 g_strdelimit (string, "\r\t", ' ');
1931 textlen = strlen (string);
1933 /* FIXME: should we check for UTF-8 here? */
1935 GST_DEBUG ("Rendering '%s'", string);
1936 gst_text_overlay_render_pangocairo (overlay, string, textlen);
1940 overlay->need_render = FALSE;
1943 static GstFlowReturn
1944 gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
1948 GstTextOverlayVAlign valign;
1949 GstTextOverlayHAlign halign;
1951 width = overlay->image_width;
1952 height = overlay->image_height;
1954 video_frame = gst_buffer_make_writable (video_frame);
1956 if (overlay->use_vertical_render)
1957 halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
1959 halign = overlay->halign;
1962 case GST_TEXT_OVERLAY_HALIGN_LEFT:
1963 xpos = overlay->xpad;
1965 case GST_TEXT_OVERLAY_HALIGN_CENTER:
1966 xpos = (overlay->width - width) / 2;
1968 case GST_TEXT_OVERLAY_HALIGN_RIGHT:
1969 xpos = overlay->width - width - overlay->xpad;
1971 case GST_TEXT_OVERLAY_HALIGN_POS:
1972 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1973 xpos = CLAMP (xpos, 0, overlay->width - width);
1980 xpos += overlay->deltax;
1982 if (overlay->use_vertical_render)
1983 valign = GST_TEXT_OVERLAY_VALIGN_TOP;
1985 valign = overlay->valign;
1988 case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
1989 ypos = overlay->height - height - overlay->ypad;
1991 case GST_TEXT_OVERLAY_VALIGN_BASELINE:
1992 ypos = overlay->height - (height + overlay->ypad);
1994 case GST_TEXT_OVERLAY_VALIGN_TOP:
1995 ypos = overlay->ypad;
1997 case GST_TEXT_OVERLAY_VALIGN_POS:
1998 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1999 ypos = CLAMP (ypos, 0, overlay->height - height);
2001 case GST_TEXT_OVERLAY_VALIGN_CENTER:
2002 ypos = (overlay->height - height) / 2;
2005 ypos = overlay->ypad;
2008 ypos += overlay->deltay;
2010 /* shaded background box */
2011 if (overlay->want_shading) {
2012 switch (overlay->format) {
2013 case GST_VIDEO_FORMAT_I420:
2014 case GST_VIDEO_FORMAT_YV12:
2015 case GST_VIDEO_FORMAT_NV12:
2016 case GST_VIDEO_FORMAT_NV21:
2017 gst_text_overlay_shade_planar_Y (overlay,
2018 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2019 ypos, ypos + overlay->image_height);
2021 case GST_VIDEO_FORMAT_AYUV:
2022 case GST_VIDEO_FORMAT_UYVY:
2023 gst_text_overlay_shade_packed_Y (overlay,
2024 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2025 ypos, ypos + overlay->image_height);
2027 case GST_VIDEO_FORMAT_xRGB:
2028 gst_text_overlay_shade_xRGB (overlay,
2029 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2030 ypos, ypos + overlay->image_height);
2032 case GST_VIDEO_FORMAT_xBGR:
2033 gst_text_overlay_shade_xBGR (overlay,
2034 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2035 ypos, ypos + overlay->image_height);
2037 case GST_VIDEO_FORMAT_BGRx:
2038 gst_text_overlay_shade_BGRx (overlay,
2039 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2040 ypos, ypos + overlay->image_height);
2042 case GST_VIDEO_FORMAT_RGBx:
2043 gst_text_overlay_shade_RGBx (overlay,
2044 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2045 ypos, ypos + overlay->image_height);
2047 case GST_VIDEO_FORMAT_ARGB:
2048 gst_text_overlay_shade_ARGB (overlay,
2049 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2050 ypos, ypos + overlay->image_height);
2052 case GST_VIDEO_FORMAT_ABGR:
2053 gst_text_overlay_shade_ABGR (overlay,
2054 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2055 ypos, ypos + overlay->image_height);
2057 case GST_VIDEO_FORMAT_RGBA:
2058 gst_text_overlay_shade_RGBA (overlay,
2059 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2060 ypos, ypos + overlay->image_height);
2062 case GST_VIDEO_FORMAT_BGRA:
2063 gst_text_overlay_shade_BGRA (overlay,
2064 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
2065 ypos, ypos + overlay->image_height);
2068 g_assert_not_reached ();
2075 if (overlay->text_image) {
2076 switch (overlay->format) {
2077 case GST_VIDEO_FORMAT_I420:
2078 case GST_VIDEO_FORMAT_YV12:
2079 gst_text_overlay_blit_I420_YV12 (overlay,
2080 GST_BUFFER_DATA (video_frame), xpos, ypos);
2082 case GST_VIDEO_FORMAT_NV12:
2083 case GST_VIDEO_FORMAT_NV21:
2084 gst_text_overlay_blit_NV12_NV21 (overlay,
2085 GST_BUFFER_DATA (video_frame), xpos, ypos);
2087 case GST_VIDEO_FORMAT_UYVY:
2088 gst_text_overlay_blit_UYVY (overlay,
2089 GST_BUFFER_DATA (video_frame), xpos, ypos);
2091 case GST_VIDEO_FORMAT_AYUV:
2092 gst_text_overlay_blit_AYUV (overlay,
2093 GST_BUFFER_DATA (video_frame), xpos, ypos);
2095 case GST_VIDEO_FORMAT_BGRx:
2096 gst_text_overlay_blit_BGRx (overlay,
2097 GST_BUFFER_DATA (video_frame), xpos, ypos);
2099 case GST_VIDEO_FORMAT_xRGB:
2100 gst_text_overlay_blit_xRGB (overlay,
2101 GST_BUFFER_DATA (video_frame), xpos, ypos);
2103 case GST_VIDEO_FORMAT_RGBx:
2104 gst_text_overlay_blit_RGBx (overlay,
2105 GST_BUFFER_DATA (video_frame), xpos, ypos);
2107 case GST_VIDEO_FORMAT_xBGR:
2108 gst_text_overlay_blit_xBGR (overlay,
2109 GST_BUFFER_DATA (video_frame), xpos, ypos);
2111 case GST_VIDEO_FORMAT_ARGB:
2112 gst_text_overlay_blit_ARGB (overlay,
2113 GST_BUFFER_DATA (video_frame), xpos, ypos);
2115 case GST_VIDEO_FORMAT_ABGR:
2116 gst_text_overlay_blit_ABGR (overlay,
2117 GST_BUFFER_DATA (video_frame), xpos, ypos);
2119 case GST_VIDEO_FORMAT_RGBA:
2120 gst_text_overlay_blit_RGBA (overlay,
2121 GST_BUFFER_DATA (video_frame), xpos, ypos);
2123 case GST_VIDEO_FORMAT_BGRA:
2124 gst_text_overlay_blit_BGRA (overlay,
2125 GST_BUFFER_DATA (video_frame), xpos, ypos);
2128 g_assert_not_reached ();
2131 return gst_pad_push (overlay->srcpad, video_frame);
2134 static GstPadLinkReturn
2135 gst_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2137 GstTextOverlay *overlay;
2139 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
2140 if (G_UNLIKELY (!overlay))
2141 return GST_PAD_LINK_REFUSED;
2143 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2145 overlay->text_linked = TRUE;
2147 gst_object_unref (overlay);
2149 return GST_PAD_LINK_OK;
2153 gst_text_overlay_text_pad_unlink (GstPad * pad)
2155 GstTextOverlay *overlay;
2157 /* don't use gst_pad_get_parent() here, will deadlock */
2158 overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2160 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2162 overlay->text_linked = FALSE;
2164 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2168 gst_text_overlay_text_event (GstPad * pad, GstEvent * event)
2170 gboolean ret = FALSE;
2171 GstTextOverlay *overlay = NULL;
2173 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
2174 if (G_UNLIKELY (!overlay)) {
2175 gst_event_unref (event);
2179 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2181 switch (GST_EVENT_TYPE (event)) {
2182 case GST_EVENT_NEWSEGMENT:{
2185 gdouble rate, applied_rate;
2186 gint64 cur, stop, time;
2188 overlay->text_eos = FALSE;
2190 gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
2191 &fmt, &cur, &stop, &time);
2193 if (fmt == GST_FORMAT_TIME) {
2194 GST_OBJECT_LOCK (overlay);
2195 gst_segment_set_newsegment_full (&overlay->text_segment, update, rate,
2196 applied_rate, GST_FORMAT_TIME, cur, stop, time);
2197 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2198 &overlay->text_segment);
2199 GST_OBJECT_UNLOCK (overlay);
2201 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2202 ("received non-TIME newsegment event on text input"));
2205 gst_event_unref (event);
2208 /* wake up the video chain, it might be waiting for a text buffer or
2209 * a text segment update */
2210 GST_OBJECT_LOCK (overlay);
2211 GST_TEXT_OVERLAY_BROADCAST (overlay);
2212 GST_OBJECT_UNLOCK (overlay);
2215 case GST_EVENT_FLUSH_STOP:
2216 GST_OBJECT_LOCK (overlay);
2217 GST_INFO_OBJECT (overlay, "text flush stop");
2218 overlay->text_flushing = FALSE;
2219 overlay->text_eos = FALSE;
2220 gst_text_overlay_pop_text (overlay);
2221 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2222 GST_OBJECT_UNLOCK (overlay);
2223 gst_event_unref (event);
2226 case GST_EVENT_FLUSH_START:
2227 GST_OBJECT_LOCK (overlay);
2228 GST_INFO_OBJECT (overlay, "text flush start");
2229 overlay->text_flushing = TRUE;
2230 GST_TEXT_OVERLAY_BROADCAST (overlay);
2231 GST_OBJECT_UNLOCK (overlay);
2232 gst_event_unref (event);
2236 GST_OBJECT_LOCK (overlay);
2237 overlay->text_eos = TRUE;
2238 GST_INFO_OBJECT (overlay, "text EOS");
2239 /* wake up the video chain, it might be waiting for a text buffer or
2240 * a text segment update */
2241 GST_TEXT_OVERLAY_BROADCAST (overlay);
2242 GST_OBJECT_UNLOCK (overlay);
2243 gst_event_unref (event);
2247 ret = gst_pad_event_default (pad, event);
2251 gst_object_unref (overlay);
2257 gst_text_overlay_video_event (GstPad * pad, GstEvent * event)
2259 gboolean ret = FALSE;
2260 GstTextOverlay *overlay = NULL;
2262 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
2263 if (G_UNLIKELY (!overlay)) {
2264 gst_event_unref (event);
2268 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2270 switch (GST_EVENT_TYPE (event)) {
2271 case GST_EVENT_NEWSEGMENT:
2275 gint64 start, stop, time;
2278 GST_DEBUG_OBJECT (overlay, "received new segment");
2280 gst_event_parse_new_segment (event, &update, &rate, &format, &start,
2283 if (format == GST_FORMAT_TIME) {
2284 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2287 gst_segment_set_newsegment (&overlay->segment, update, rate, format,
2290 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2291 ("received non-TIME newsegment event on video input"));
2294 ret = gst_pad_event_default (pad, event);
2298 GST_OBJECT_LOCK (overlay);
2299 GST_INFO_OBJECT (overlay, "video EOS");
2300 overlay->video_eos = TRUE;
2301 GST_OBJECT_UNLOCK (overlay);
2302 ret = gst_pad_event_default (pad, event);
2304 case GST_EVENT_FLUSH_START:
2305 GST_OBJECT_LOCK (overlay);
2306 GST_INFO_OBJECT (overlay, "video flush start");
2307 overlay->video_flushing = TRUE;
2308 GST_TEXT_OVERLAY_BROADCAST (overlay);
2309 GST_OBJECT_UNLOCK (overlay);
2310 ret = gst_pad_event_default (pad, event);
2312 case GST_EVENT_FLUSH_STOP:
2313 GST_OBJECT_LOCK (overlay);
2314 GST_INFO_OBJECT (overlay, "video flush stop");
2315 overlay->video_flushing = FALSE;
2316 overlay->video_eos = FALSE;
2317 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2318 GST_OBJECT_UNLOCK (overlay);
2319 ret = gst_pad_event_default (pad, event);
2322 ret = gst_pad_event_default (pad, event);
2326 gst_object_unref (overlay);
2331 static GstFlowReturn
2332 gst_text_overlay_video_bufferalloc (GstPad * pad, guint64 offset, guint size,
2333 GstCaps * caps, GstBuffer ** buffer)
2335 GstTextOverlay *overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
2336 GstFlowReturn ret = GST_FLOW_WRONG_STATE;
2339 if (G_UNLIKELY (!overlay))
2340 return GST_FLOW_WRONG_STATE;
2342 GST_OBJECT_LOCK (overlay);
2343 allocpad = overlay->srcpad ? gst_object_ref (overlay->srcpad) : NULL;
2344 GST_OBJECT_UNLOCK (overlay);
2347 ret = gst_pad_alloc_buffer (allocpad, offset, size, caps, buffer);
2348 gst_object_unref (allocpad);
2351 gst_object_unref (overlay);
2355 /* Called with lock held */
2357 gst_text_overlay_pop_text (GstTextOverlay * overlay)
2359 g_return_if_fail (GST_IS_TEXT_OVERLAY (overlay));
2361 if (overlay->text_buffer) {
2362 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2363 overlay->text_buffer);
2364 gst_buffer_unref (overlay->text_buffer);
2365 overlay->text_buffer = NULL;
2368 /* Let the text task know we used that buffer */
2369 GST_TEXT_OVERLAY_BROADCAST (overlay);
2372 /* We receive text buffers here. If they are out of segment we just ignore them.
2373 If the buffer is in our segment we keep it internally except if another one
2374 is already waiting here, in that case we wait that it gets kicked out */
2375 static GstFlowReturn
2376 gst_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
2378 GstFlowReturn ret = GST_FLOW_OK;
2379 GstTextOverlay *overlay = NULL;
2380 gboolean in_seg = FALSE;
2381 gint64 clip_start = 0, clip_stop = 0;
2383 overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2385 GST_OBJECT_LOCK (overlay);
2387 if (overlay->text_flushing) {
2388 GST_OBJECT_UNLOCK (overlay);
2389 ret = GST_FLOW_WRONG_STATE;
2390 GST_LOG_OBJECT (overlay, "text flushing");
2394 if (overlay->text_eos) {
2395 GST_OBJECT_UNLOCK (overlay);
2396 ret = GST_FLOW_UNEXPECTED;
2397 GST_LOG_OBJECT (overlay, "text EOS");
2401 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2402 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2403 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2404 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2405 GST_BUFFER_DURATION (buffer)));
2407 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2410 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2411 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2413 stop = GST_CLOCK_TIME_NONE;
2415 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2416 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2422 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2423 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2424 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2425 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2427 if (overlay->text_buffer
2428 && (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer)
2429 || !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer))) {
2430 gst_text_overlay_pop_text (overlay);
2432 /* Wait for the previous buffer to go away */
2433 while (overlay->text_buffer != NULL) {
2434 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2435 GST_DEBUG_PAD_NAME (pad));
2436 GST_TEXT_OVERLAY_WAIT (overlay);
2437 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2438 if (overlay->text_flushing) {
2439 GST_OBJECT_UNLOCK (overlay);
2440 ret = GST_FLOW_WRONG_STATE;
2446 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2447 gst_segment_set_last_stop (&overlay->text_segment, GST_FORMAT_TIME,
2450 overlay->text_buffer = gst_buffer_ref (buffer);
2451 /* That's a new text buffer we need to render */
2452 overlay->need_render = TRUE;
2454 /* in case the video chain is waiting for a text buffer, wake it up */
2455 GST_TEXT_OVERLAY_BROADCAST (overlay);
2458 GST_OBJECT_UNLOCK (overlay);
2462 gst_buffer_unref (buffer);
2466 static GstFlowReturn
2467 gst_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
2469 GstTextOverlayClass *klass;
2470 GstTextOverlay *overlay;
2471 GstFlowReturn ret = GST_FLOW_OK;
2472 gboolean in_seg = FALSE;
2473 gint64 start, stop, clip_start = 0, clip_stop = 0;
2476 overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2477 klass = GST_TEXT_OVERLAY_GET_CLASS (overlay);
2479 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2480 goto missing_timestamp;
2482 /* ignore buffers that are outside of the current segment */
2483 start = GST_BUFFER_TIMESTAMP (buffer);
2485 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2486 stop = GST_CLOCK_TIME_NONE;
2488 stop = start + GST_BUFFER_DURATION (buffer);
2491 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2492 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2493 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2495 /* segment_clip() will adjust start unconditionally to segment_start if
2496 * no stop time is provided, so handle this ourselves */
2497 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2498 goto out_of_segment;
2500 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2501 &clip_start, &clip_stop);
2504 goto out_of_segment;
2506 /* if the buffer is only partially in the segment, fix up stamps */
2507 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2508 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2509 buffer = gst_buffer_make_metadata_writable (buffer);
2510 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2512 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2515 /* now, after we've done the clipping, fix up end time if there's no
2516 * duration (we only use those estimated values internally though, we
2517 * don't want to set bogus values on the buffer itself) */
2520 gint fps_num, fps_denom;
2522 s = gst_caps_get_structure (GST_PAD_CAPS (pad), 0);
2523 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2524 fps_num && fps_denom) {
2525 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2526 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2528 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2529 stop = start + 1; /* we need to assume some interval */
2533 gst_object_sync_values (G_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2537 GST_OBJECT_LOCK (overlay);
2539 if (overlay->video_flushing)
2542 if (overlay->video_eos)
2545 if (overlay->silent && !overlay->text_linked) {
2546 GST_OBJECT_UNLOCK (overlay);
2547 ret = gst_pad_push (overlay->srcpad, buffer);
2549 /* Update last_stop */
2550 gst_segment_set_last_stop (&overlay->segment, GST_FORMAT_TIME, clip_start);
2555 /* Text pad not linked, rendering internal text */
2556 if (!overlay->text_linked) {
2557 if (klass->get_text) {
2558 text = klass->get_text (overlay, buffer);
2560 text = g_strdup (overlay->default_text);
2563 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2564 "text: '%s'", GST_STR_NULL (text));
2566 GST_OBJECT_UNLOCK (overlay);
2568 if (text != NULL && *text != '\0') {
2569 /* Render and push */
2570 gst_text_overlay_render_text (overlay, text, -1);
2571 ret = gst_text_overlay_push_frame (overlay, buffer);
2573 /* Invalid or empty string */
2574 ret = gst_pad_push (overlay->srcpad, buffer);
2577 /* Text pad linked, check if we have a text buffer queued */
2578 if (overlay->text_buffer) {
2579 gboolean pop_text = FALSE, valid_text_time = TRUE;
2580 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2581 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2582 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2583 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2584 GstClockTime vid_running_time, vid_running_time_end;
2586 /* if the text buffer isn't stamped right, pop it off the
2587 * queue and display it for the current video frame only */
2588 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2589 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2590 GST_WARNING_OBJECT (overlay,
2591 "Got text buffer with invalid timestamp or duration");
2592 valid_text_time = FALSE;
2594 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2595 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2599 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2601 vid_running_time_end =
2602 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2605 /* If timestamp and duration are valid */
2606 if (valid_text_time) {
2608 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2610 text_running_time_end =
2611 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2615 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2616 GST_TIME_ARGS (text_running_time),
2617 GST_TIME_ARGS (text_running_time_end));
2618 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2619 GST_TIME_ARGS (vid_running_time),
2620 GST_TIME_ARGS (vid_running_time_end));
2622 /* Text too old or in the future */
2623 if (valid_text_time && text_running_time_end <= vid_running_time) {
2624 /* text buffer too old, get rid of it and do nothing */
2625 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2627 gst_text_overlay_pop_text (overlay);
2628 GST_OBJECT_UNLOCK (overlay);
2629 goto wait_for_text_buf;
2630 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2631 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2632 GST_OBJECT_UNLOCK (overlay);
2633 /* Push the video frame */
2634 ret = gst_pad_push (overlay->srcpad, buffer);
2635 } else if (overlay->silent) {
2636 GST_LOG_OBJECT (overlay, "silent enabled, pushing video buf");
2637 GST_OBJECT_UNLOCK (overlay);
2638 /* Push the video frame */
2639 ret = gst_pad_push (overlay->srcpad, buffer);
2644 in_text = (gchar *) GST_BUFFER_DATA (overlay->text_buffer);
2645 in_size = GST_BUFFER_SIZE (overlay->text_buffer);
2647 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2648 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2649 * here on purpose, this is something that needs fixing upstream */
2650 if (!g_utf8_validate (in_text, in_size, NULL)) {
2651 const gchar *end = NULL;
2653 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2654 in_text = g_strndup (in_text, in_size);
2655 while (!g_utf8_validate (in_text, in_size, &end) && end)
2656 *((gchar *) end) = '*';
2659 /* Get the string */
2660 if (overlay->have_pango_markup) {
2661 text = g_strndup (in_text, in_size);
2663 text = g_markup_escape_text (in_text, in_size);
2666 if (text != NULL && *text != '\0') {
2667 gint text_len = strlen (text);
2669 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2670 text[text_len - 1] == '\r')) {
2673 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2674 gst_text_overlay_render_text (overlay, text, text_len);
2676 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2677 gst_text_overlay_render_text (overlay, " ", 1);
2680 if (in_text != (gchar *) GST_BUFFER_DATA (overlay->text_buffer))
2683 GST_OBJECT_UNLOCK (overlay);
2684 ret = gst_text_overlay_push_frame (overlay, buffer);
2686 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2687 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2692 GST_OBJECT_LOCK (overlay);
2693 gst_text_overlay_pop_text (overlay);
2694 GST_OBJECT_UNLOCK (overlay);
2697 gboolean wait_for_text_buf = TRUE;
2699 if (overlay->text_eos)
2700 wait_for_text_buf = FALSE;
2702 if (!overlay->wait_text)
2703 wait_for_text_buf = FALSE;
2705 /* Text pad linked, but no text buffer available - what now? */
2706 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2707 GstClockTime text_start_running_time, text_last_stop_running_time;
2708 GstClockTime vid_running_time;
2711 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2712 GST_BUFFER_TIMESTAMP (buffer));
2713 text_start_running_time =
2714 gst_segment_to_running_time (&overlay->text_segment,
2715 GST_FORMAT_TIME, overlay->text_segment.start);
2716 text_last_stop_running_time =
2717 gst_segment_to_running_time (&overlay->text_segment,
2718 GST_FORMAT_TIME, overlay->text_segment.last_stop);
2720 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2721 vid_running_time < text_start_running_time) ||
2722 (GST_CLOCK_TIME_IS_VALID (text_last_stop_running_time) &&
2723 vid_running_time < text_last_stop_running_time)) {
2724 wait_for_text_buf = FALSE;
2728 if (wait_for_text_buf) {
2729 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2730 GST_TEXT_OVERLAY_WAIT (overlay);
2731 GST_DEBUG_OBJECT (overlay, "resuming");
2732 GST_OBJECT_UNLOCK (overlay);
2733 goto wait_for_text_buf;
2735 GST_OBJECT_UNLOCK (overlay);
2736 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2737 ret = gst_pad_push (overlay->srcpad, buffer);
2744 /* Update last_stop */
2745 gst_segment_set_last_stop (&overlay->segment, GST_FORMAT_TIME, clip_start);
2751 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2752 gst_buffer_unref (buffer);
2758 GST_OBJECT_UNLOCK (overlay);
2759 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2760 gst_buffer_unref (buffer);
2761 return GST_FLOW_WRONG_STATE;
2765 GST_OBJECT_UNLOCK (overlay);
2766 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2767 gst_buffer_unref (buffer);
2768 return GST_FLOW_UNEXPECTED;
2772 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2773 gst_buffer_unref (buffer);
2778 static GstStateChangeReturn
2779 gst_text_overlay_change_state (GstElement * element, GstStateChange transition)
2781 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2782 GstTextOverlay *overlay = GST_TEXT_OVERLAY (element);
2784 switch (transition) {
2785 case GST_STATE_CHANGE_PAUSED_TO_READY:
2786 GST_OBJECT_LOCK (overlay);
2787 overlay->text_flushing = TRUE;
2788 overlay->video_flushing = TRUE;
2789 /* pop_text will broadcast on the GCond and thus also make the video
2790 * chain exit if it's waiting for a text buffer */
2791 gst_text_overlay_pop_text (overlay);
2792 GST_OBJECT_UNLOCK (overlay);
2798 ret = parent_class->change_state (element, transition);
2799 if (ret == GST_STATE_CHANGE_FAILURE)
2802 switch (transition) {
2803 case GST_STATE_CHANGE_READY_TO_PAUSED:
2804 GST_OBJECT_LOCK (overlay);
2805 overlay->text_flushing = FALSE;
2806 overlay->video_flushing = FALSE;
2807 overlay->video_eos = FALSE;
2808 overlay->text_eos = FALSE;
2809 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2810 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2811 GST_OBJECT_UNLOCK (overlay);
2821 plugin_init (GstPlugin * plugin)
2823 gst_controller_init (NULL, NULL);
2825 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2826 GST_TYPE_TEXT_OVERLAY) ||
2827 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2828 GST_TYPE_TIME_OVERLAY) ||
2829 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2830 GST_TYPE_CLOCK_OVERLAY) ||
2831 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2832 GST_TYPE_TEXT_RENDER)) {
2836 /*texttestsrc_plugin_init(module, plugin); */
2838 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2843 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2844 "pango", "Pango-based text rendering and overlay", plugin_init,
2845 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)