2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2003> David Schleef <ds@schleef.org>
4 * Copyright (C) <2006> Julien Moutte <julien@moutte.net>
5 * Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com>
6 * Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net>
7 * Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
26 * SECTION:element-textoverlay
27 * @see_also: #GstTextRender, #GstClockOverlay, #GstTimeOverlay, #GstSubParse
29 * This plugin renders text on top of a video stream. This can be either
30 * static text or text from buffers received on the text sink pad, e.g.
31 * as produced by the subparse element. If the text sink pad is not linked,
32 * the text set via the "text" property will be rendered. If the text sink
33 * pad is linked, text will be rendered as it is received on that pad,
34 * honouring and matching the buffer timestamps of both input streams.
36 * The text can contain newline characters and text wrapping is enabled by
40 * <title>Example launch lines</title>
42 * gst-launch -v videotestsrc ! textoverlay text="Room A" valign=top halign=left ! xvimagesink
43 * ]| Here is a simple pipeline that displays a static text in the top left
44 * corner of the video picture
46 * gst-launch -v filesrc location=subtitles.srt ! subparse ! txt. videotestsrc ! timeoverlay ! textoverlay name=txt shaded-background=yes ! xvimagesink
47 * ]| Here is another pipeline that displays subtitles from an .srt subtitle
48 * file, centered at the bottom of the picture and with a rectangular shading
49 * around the text in the background:
51 * If you do not have such a subtitle file, create one looking like this
55 * 00:00:03,000 --> 00:00:05,000
59 * 00:00:08,000 --> 00:00:13,000
60 * Yes, this is a subtitle. Don't
61 * you like it? (8-13s)
64 * 00:00:18,826 --> 00:01:02,886
65 * Uh? What are you talking about?
66 * I don't understand (18-62s)
72 /* FIXME: alloc segment as part of instance struct */
78 #include <gst/video/video.h>
80 #include "gstbasetextoverlay.h"
81 #include "gsttextoverlay.h"
82 #include "gsttimeoverlay.h"
83 #include "gstclockoverlay.h"
84 #include "gsttextrender.h"
88 * - use proper strides and offset for I420
89 * - if text is wider than the video picture, it does not get
90 * clipped properly during blitting (if wrapping is disabled)
91 * - make 'shading_value' a property (or enum: light/normal/dark/verydark)?
94 GST_DEBUG_CATEGORY (pango_debug);
95 #define GST_CAT_DEFAULT pango_debug
97 #define DEFAULT_PROP_TEXT ""
98 #define DEFAULT_PROP_SHADING FALSE
99 #define DEFAULT_PROP_VALIGNMENT GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE
100 #define DEFAULT_PROP_HALIGNMENT GST_BASE_TEXT_OVERLAY_HALIGN_CENTER
101 #define DEFAULT_PROP_VALIGN "baseline"
102 #define DEFAULT_PROP_HALIGN "center"
103 #define DEFAULT_PROP_XPAD 25
104 #define DEFAULT_PROP_YPAD 25
105 #define DEFAULT_PROP_DELTAX 0
106 #define DEFAULT_PROP_DELTAY 0
107 #define DEFAULT_PROP_XPOS 0.5
108 #define DEFAULT_PROP_YPOS 0.5
109 #define DEFAULT_PROP_WRAP_MODE GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR
110 #define DEFAULT_PROP_FONT_DESC ""
111 #define DEFAULT_PROP_SILENT FALSE
112 #define DEFAULT_PROP_LINE_ALIGNMENT GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER
113 #define DEFAULT_PROP_WAIT_TEXT TRUE
114 #define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE
115 #define DEFAULT_PROP_VERTICAL_RENDER FALSE
116 #define DEFAULT_PROP_COLOR 0xffffffff
117 #define DEFAULT_PROP_OUTLINE_COLOR 0xff000000
119 /* make a property of me */
120 #define DEFAULT_SHADING_VALUE -80
122 #define MINIMUM_OUTLINE_OFFSET 1.0
123 #define DEFAULT_SCALE_BASIS 640
125 #define COMP_Y(ret, r, g, b) \
127 ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \
128 ret = CLAMP (ret, 0, 255); \
131 #define COMP_U(ret, r, g, b) \
133 ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \
134 ret = CLAMP (ret, 0, 255); \
137 #define COMP_V(ret, r, g, b) \
139 ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \
140 ret = CLAMP (ret, 0, 255); \
143 #define BLEND(ret, alpha, v0, v1) \
145 ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \
148 #define OVER(ret, alphaA, Ca, alphaB, Cb, alphaNew) \
151 _tmp = (Ca * alphaA + Cb * alphaB * (255 - alphaA) / 255) / alphaNew; \
152 ret = CLAMP (_tmp, 0, 255); \
155 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
156 # define CAIRO_ARGB_A 3
157 # define CAIRO_ARGB_R 2
158 # define CAIRO_ARGB_G 1
159 # define CAIRO_ARGB_B 0
161 # define CAIRO_ARGB_A 0
162 # define CAIRO_ARGB_R 1
163 # define CAIRO_ARGB_G 2
164 # define CAIRO_ARGB_B 3
172 PROP_VALIGN, /* deprecated */
173 PROP_HALIGN, /* deprecated */
187 PROP_AUTO_ADJUST_SIZE,
188 PROP_VERTICAL_RENDER,
195 #define VIDEO_FORMATS "{ BGRx, RGBx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, AYUV, I420, UYVY, NV12, NV21 } "
197 static GstStaticPadTemplate src_template_factory =
198 GST_STATIC_PAD_TEMPLATE ("src",
201 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
204 static GstStaticPadTemplate video_sink_template_factory =
205 GST_STATIC_PAD_TEMPLATE ("video_sink",
208 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
211 #define GST_TYPE_BASE_TEXT_OVERLAY_VALIGN (gst_base_text_overlay_valign_get_type())
213 gst_base_text_overlay_valign_get_type (void)
215 static GType base_text_overlay_valign_type = 0;
216 static const GEnumValue base_text_overlay_valign[] = {
217 {GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"},
218 {GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"},
219 {GST_BASE_TEXT_OVERLAY_VALIGN_TOP, "top", "top"},
220 {GST_BASE_TEXT_OVERLAY_VALIGN_POS, "position", "position"},
221 {GST_BASE_TEXT_OVERLAY_VALIGN_CENTER, "center", "center"},
225 if (!base_text_overlay_valign_type) {
226 base_text_overlay_valign_type =
227 g_enum_register_static ("GstBaseTextOverlayVAlign",
228 base_text_overlay_valign);
230 return base_text_overlay_valign_type;
233 #define GST_TYPE_BASE_TEXT_OVERLAY_HALIGN (gst_base_text_overlay_halign_get_type())
235 gst_base_text_overlay_halign_get_type (void)
237 static GType base_text_overlay_halign_type = 0;
238 static const GEnumValue base_text_overlay_halign[] = {
239 {GST_BASE_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"},
240 {GST_BASE_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"},
241 {GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"},
242 {GST_BASE_TEXT_OVERLAY_HALIGN_POS, "position", "position"},
246 if (!base_text_overlay_halign_type) {
247 base_text_overlay_halign_type =
248 g_enum_register_static ("GstBaseTextOverlayHAlign",
249 base_text_overlay_halign);
251 return base_text_overlay_halign_type;
255 #define GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE (gst_base_text_overlay_wrap_mode_get_type())
257 gst_base_text_overlay_wrap_mode_get_type (void)
259 static GType base_text_overlay_wrap_mode_type = 0;
260 static const GEnumValue base_text_overlay_wrap_mode[] = {
261 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"},
262 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"},
263 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"},
264 {GST_BASE_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"},
268 if (!base_text_overlay_wrap_mode_type) {
269 base_text_overlay_wrap_mode_type =
270 g_enum_register_static ("GstBaseTextOverlayWrapMode",
271 base_text_overlay_wrap_mode);
273 return base_text_overlay_wrap_mode_type;
276 #define GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN (gst_base_text_overlay_line_align_get_type())
278 gst_base_text_overlay_line_align_get_type (void)
280 static GType base_text_overlay_line_align_type = 0;
281 static const GEnumValue base_text_overlay_line_align[] = {
282 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"},
283 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"},
284 {GST_BASE_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"},
288 if (!base_text_overlay_line_align_type) {
289 base_text_overlay_line_align_type =
290 g_enum_register_static ("GstBaseTextOverlayLineAlign",
291 base_text_overlay_line_align);
293 return base_text_overlay_line_align_type;
296 #define GST_BASE_TEXT_OVERLAY_GET_COND(ov) (((GstBaseTextOverlay *)ov)->cond)
297 #define GST_BASE_TEXT_OVERLAY_WAIT(ov) (g_cond_wait (&GST_BASE_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov)))
298 #define GST_BASE_TEXT_OVERLAY_SIGNAL(ov) (g_cond_signal (&GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
299 #define GST_BASE_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (&GST_BASE_TEXT_OVERLAY_GET_COND (ov)))
301 static GstElementClass *parent_class = NULL;
302 static void gst_base_text_overlay_base_init (gpointer g_class);
303 static void gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass);
304 static void gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
305 GstBaseTextOverlayClass * klass);
307 static GstStateChangeReturn gst_base_text_overlay_change_state (GstElement *
308 element, GstStateChange transition);
310 static GstCaps *gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter);
311 static gboolean gst_base_text_overlay_setcaps (GstBaseTextOverlay * overlay,
313 static gboolean gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay,
315 static gboolean gst_base_text_overlay_src_event (GstPad * pad,
316 GstObject * parent, GstEvent * event);
317 static gboolean gst_base_text_overlay_src_query (GstPad * pad,
318 GstObject * parent, GstQuery * query);
320 static gboolean gst_base_text_overlay_video_event (GstPad * pad,
321 GstObject * parent, GstEvent * event);
322 static gboolean gst_base_text_overlay_video_query (GstPad * pad,
323 GstObject * parent, GstQuery * query);
324 static GstFlowReturn gst_base_text_overlay_video_chain (GstPad * pad,
325 GstObject * parent, GstBuffer * buffer);
327 static gboolean gst_base_text_overlay_text_event (GstPad * pad,
328 GstObject * parent, GstEvent * event);
329 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
330 GstObject * parent, GstBuffer * buffer);
331 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
333 static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
334 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
335 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
338 static void gst_base_text_overlay_finalize (GObject * object);
339 static void gst_base_text_overlay_set_property (GObject * object, guint prop_id,
340 const GValue * value, GParamSpec * pspec);
341 static void gst_base_text_overlay_get_property (GObject * object, guint prop_id,
342 GValue * value, GParamSpec * pspec);
344 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
345 PangoFontDescription * desc);
348 gst_base_text_overlay_get_type (void)
350 static GType type = 0;
352 if (g_once_init_enter ((gsize *) & type)) {
353 static const GTypeInfo info = {
354 sizeof (GstBaseTextOverlayClass),
355 (GBaseInitFunc) gst_base_text_overlay_base_init,
357 (GClassInitFunc) gst_base_text_overlay_class_init,
360 sizeof (GstBaseTextOverlay),
362 (GInstanceInitFunc) gst_base_text_overlay_init,
365 g_once_init_leave ((gsize *) & type,
366 g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTextOverlay", &info,
374 gst_base_text_overlay_get_text (GstBaseTextOverlay * overlay,
375 GstBuffer * video_frame)
377 return g_strdup (overlay->default_text);
381 gst_base_text_overlay_base_init (gpointer g_class)
383 GstBaseTextOverlayClass *klass = GST_BASE_TEXT_OVERLAY_CLASS (g_class);
384 PangoFontMap *fontmap;
386 /* Only lock for the subclasses here, the base class
387 * doesn't have this mutex yet and it's not necessary
389 if (klass->pango_lock)
390 g_mutex_lock (klass->pango_lock);
391 fontmap = pango_cairo_font_map_get_default ();
392 klass->pango_context =
393 pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
394 if (klass->pango_lock)
395 g_mutex_unlock (klass->pango_lock);
399 gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
401 GObjectClass *gobject_class;
402 GstElementClass *gstelement_class;
404 gobject_class = (GObjectClass *) klass;
405 gstelement_class = (GstElementClass *) klass;
407 parent_class = g_type_class_peek_parent (klass);
409 gobject_class->finalize = gst_base_text_overlay_finalize;
410 gobject_class->set_property = gst_base_text_overlay_set_property;
411 gobject_class->get_property = gst_base_text_overlay_get_property;
413 gst_element_class_add_pad_template (gstelement_class,
414 gst_static_pad_template_get (&src_template_factory));
415 gst_element_class_add_pad_template (gstelement_class,
416 gst_static_pad_template_get (&video_sink_template_factory));
418 gstelement_class->change_state =
419 GST_DEBUG_FUNCPTR (gst_base_text_overlay_change_state);
421 klass->pango_lock = g_slice_new (GMutex);
422 g_mutex_init (klass->pango_lock);
424 klass->get_text = gst_base_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));
434 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
435 g_param_spec_enum ("valignment", "vertical alignment",
436 "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
437 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
438 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
439 g_param_spec_enum ("halignment", "horizontal alignment",
440 "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
441 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
442 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
443 g_param_spec_string ("valign", "vertical alignment",
444 "Vertical alignment of the text (deprecated; use valignment)",
445 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
446 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
447 g_param_spec_string ("halign", "horizontal alignment",
448 "Horizontal alignment of the text (deprecated; use halignment)",
449 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
450 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
451 g_param_spec_int ("xpad", "horizontal paddding",
452 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
453 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
454 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
455 g_param_spec_int ("ypad", "vertical padding",
456 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
457 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
458 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
459 g_param_spec_int ("deltax", "X position modifier",
460 "Shift X position to the left or to the right. Unit is pixels.",
461 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
462 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
463 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
464 g_param_spec_int ("deltay", "Y position modifier",
465 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
466 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
468 * GstBaseTextOverlay:xpos
470 * Horizontal position of the rendered text when using positioned alignment.
474 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
475 g_param_spec_double ("xpos", "horizontal position",
476 "Horizontal position when using position alignment", 0, 1.0,
478 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
480 * GstBaseTextOverlay:ypos
482 * Vertical position of the rendered text when using positioned alignment.
486 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
487 g_param_spec_double ("ypos", "vertical position",
488 "Vertical position when using position alignment", 0, 1.0,
490 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
491 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
492 g_param_spec_enum ("wrap-mode", "wrap mode",
493 "Whether to wrap the text and if so how.",
494 GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
495 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
496 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
497 g_param_spec_string ("font-desc", "font description",
498 "Pango font description of font to be used for rendering. "
499 "See documentation of pango_font_description_from_string "
500 "for syntax.", DEFAULT_PROP_FONT_DESC,
501 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
503 * GstBaseTextOverlay:color
505 * Color of the rendered text.
509 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
510 g_param_spec_uint ("color", "Color",
511 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
513 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
515 * GstTextOverlay:outline-color
517 * Color of the outline of the rendered text.
521 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
522 g_param_spec_uint ("outline-color", "Text Outline Color",
523 "Color to use for outline the text (big-endian ARGB).", 0,
524 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
525 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
528 * GstBaseTextOverlay:line-alignment
530 * Alignment of text lines relative to each other (for multi-line text)
534 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
535 g_param_spec_enum ("line-alignment", "line alignment",
536 "Alignment of text lines relative to each other.",
537 GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
538 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
540 * GstBaseTextOverlay:silent
542 * If set, no text is rendered. Useful to switch off text rendering
543 * temporarily without removing the textoverlay element from the pipeline.
547 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
548 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
549 g_param_spec_boolean ("silent", "silent",
550 "Whether to render the text string",
552 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
554 * GstBaseTextOverlay:wait-text
556 * If set, the video will block until a subtitle is received on the text pad.
557 * If video and subtitles are sent in sync, like from the same demuxer, this
558 * property should be set.
562 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
563 g_param_spec_boolean ("wait-text", "Wait Text",
564 "Whether to wait for subtitles",
565 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
567 g_object_class_install_property (G_OBJECT_CLASS (klass),
568 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
569 "Automatically adjust font size to screen-size.",
570 DEFAULT_PROP_AUTO_ADJUST_SIZE,
571 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
573 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
574 g_param_spec_boolean ("vertical-render", "vertical render",
575 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
576 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
580 gst_base_text_overlay_finalize (GObject * object)
582 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
584 g_free (overlay->default_text);
586 if (overlay->text_image) {
587 g_free (overlay->text_image);
588 overlay->text_image = NULL;
591 if (overlay->layout) {
592 g_object_unref (overlay->layout);
593 overlay->layout = NULL;
596 if (overlay->text_buffer) {
597 gst_buffer_unref (overlay->text_buffer);
598 overlay->text_buffer = NULL;
601 g_cond_clear (&overlay->cond);
603 G_OBJECT_CLASS (parent_class)->finalize (object);
607 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
608 GstBaseTextOverlayClass * klass)
610 GstPadTemplate *template;
611 PangoFontDescription *desc;
614 template = gst_static_pad_template_get (&video_sink_template_factory);
615 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
616 gst_object_unref (template);
617 gst_pad_set_event_function (overlay->video_sinkpad,
618 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_event));
619 gst_pad_set_chain_function (overlay->video_sinkpad,
620 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
621 gst_pad_set_query_function (overlay->video_sinkpad,
622 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_query));
623 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
626 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
630 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
631 gst_object_unref (template);
633 gst_pad_set_event_function (overlay->text_sinkpad,
634 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
635 gst_pad_set_chain_function (overlay->text_sinkpad,
636 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
637 gst_pad_set_link_function (overlay->text_sinkpad,
638 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
639 gst_pad_set_unlink_function (overlay->text_sinkpad,
640 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
641 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
645 template = gst_static_pad_template_get (&src_template_factory);
646 overlay->srcpad = gst_pad_new_from_template (template, "src");
647 gst_object_unref (template);
648 gst_pad_set_event_function (overlay->srcpad,
649 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_event));
650 gst_pad_set_query_function (overlay->srcpad,
651 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_query));
652 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
654 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
655 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
657 pango_layout_new (GST_BASE_TEXT_OVERLAY_GET_CLASS
658 (overlay)->pango_context);
660 pango_context_get_font_description (GST_BASE_TEXT_OVERLAY_GET_CLASS
661 (overlay)->pango_context);
662 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
664 overlay->color = DEFAULT_PROP_COLOR;
665 overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
666 overlay->halign = DEFAULT_PROP_HALIGNMENT;
667 overlay->valign = DEFAULT_PROP_VALIGNMENT;
668 overlay->xpad = DEFAULT_PROP_XPAD;
669 overlay->ypad = DEFAULT_PROP_YPAD;
670 overlay->deltax = DEFAULT_PROP_DELTAX;
671 overlay->deltay = DEFAULT_PROP_DELTAY;
672 overlay->xpos = DEFAULT_PROP_XPOS;
673 overlay->ypos = DEFAULT_PROP_YPOS;
675 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
677 overlay->want_shading = DEFAULT_PROP_SHADING;
678 overlay->shading_value = DEFAULT_SHADING_VALUE;
679 overlay->silent = DEFAULT_PROP_SILENT;
680 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
681 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
683 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
684 overlay->need_render = TRUE;
685 overlay->text_image = NULL;
686 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
687 gst_base_text_overlay_update_render_mode (overlay);
689 overlay->text_buffer = NULL;
690 overlay->text_linked = FALSE;
691 g_cond_init (&overlay->cond);
692 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
693 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
697 gst_base_text_overlay_update_wrap_mode (GstBaseTextOverlay * overlay)
699 if (overlay->wrap_mode == GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE) {
700 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
701 pango_layout_set_width (overlay->layout, -1);
705 if (overlay->auto_adjust_size) {
706 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
707 if (overlay->use_vertical_render) {
708 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
712 (overlay->use_vertical_render ? overlay->height : overlay->width) *
716 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
717 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
718 pango_layout_set_width (overlay->layout, width);
719 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
724 gst_base_text_overlay_update_render_mode (GstBaseTextOverlay * overlay)
726 PangoMatrix matrix = PANGO_MATRIX_INIT;
727 PangoContext *context = pango_layout_get_context (overlay->layout);
729 if (overlay->use_vertical_render) {
730 pango_matrix_rotate (&matrix, -90);
731 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
732 pango_context_set_matrix (context, &matrix);
733 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
735 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
736 pango_context_set_matrix (context, &matrix);
737 pango_layout_set_alignment (overlay->layout, overlay->line_align);
742 gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay, GstCaps * caps)
744 GstStructure *structure;
746 structure = gst_caps_get_structure (caps, 0);
747 overlay->have_pango_markup =
748 gst_structure_has_name (structure, "text/x-pango-markup");
753 /* FIXME: upstream nego (e.g. when the video window is resized) */
756 gst_base_text_overlay_setcaps (GstBaseTextOverlay * overlay, GstCaps * caps)
759 gboolean ret = FALSE;
761 if (!gst_video_info_from_caps (&info, caps))
764 overlay->info = info;
765 overlay->format = GST_VIDEO_INFO_FORMAT (&info);
766 overlay->width = GST_VIDEO_INFO_WIDTH (&info);
767 overlay->height = GST_VIDEO_INFO_HEIGHT (&info);
769 ret = gst_pad_push_event (overlay->srcpad, gst_event_new_caps (caps));
772 GST_OBJECT_LOCK (overlay);
773 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
774 gst_base_text_overlay_update_wrap_mode (overlay);
775 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
776 GST_OBJECT_UNLOCK (overlay);
784 GST_DEBUG_OBJECT (overlay, "could not parse caps");
790 gst_base_text_overlay_set_property (GObject * object, guint prop_id,
791 const GValue * value, GParamSpec * pspec)
793 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
795 GST_OBJECT_LOCK (overlay);
798 g_free (overlay->default_text);
799 overlay->default_text = g_value_dup_string (value);
800 overlay->need_render = TRUE;
803 overlay->want_shading = g_value_get_boolean (value);
806 overlay->xpad = g_value_get_int (value);
809 overlay->ypad = g_value_get_int (value);
812 overlay->deltax = g_value_get_int (value);
815 overlay->deltay = g_value_get_int (value);
818 overlay->xpos = g_value_get_double (value);
821 overlay->ypos = g_value_get_double (value);
824 const gchar *s = g_value_get_string (value);
826 if (s && g_ascii_strcasecmp (s, "left") == 0)
827 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_LEFT;
828 else if (s && g_ascii_strcasecmp (s, "center") == 0)
829 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_CENTER;
830 else if (s && g_ascii_strcasecmp (s, "right") == 0)
831 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
833 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
838 const gchar *s = g_value_get_string (value);
840 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
841 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE;
842 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
843 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM;
844 else if (s && g_ascii_strcasecmp (s, "top") == 0)
845 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
847 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
851 case PROP_VALIGNMENT:
852 overlay->valign = g_value_get_enum (value);
854 case PROP_HALIGNMENT:
855 overlay->halign = g_value_get_enum (value);
858 overlay->wrap_mode = g_value_get_enum (value);
859 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
860 gst_base_text_overlay_update_wrap_mode (overlay);
861 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
865 PangoFontDescription *desc;
866 const gchar *fontdesc_str;
868 fontdesc_str = g_value_get_string (value);
869 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
870 desc = pango_font_description_from_string (fontdesc_str);
872 GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
873 pango_layout_set_font_description (overlay->layout, desc);
874 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
875 pango_font_description_free (desc);
877 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
880 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
884 overlay->color = g_value_get_uint (value);
886 case PROP_OUTLINE_COLOR:
887 overlay->outline_color = g_value_get_uint (value);
890 overlay->silent = g_value_get_boolean (value);
892 case PROP_LINE_ALIGNMENT:
893 overlay->line_align = g_value_get_enum (value);
894 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
895 pango_layout_set_alignment (overlay->layout,
896 (PangoAlignment) overlay->line_align);
897 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
900 overlay->wait_text = g_value_get_boolean (value);
902 case PROP_AUTO_ADJUST_SIZE:
903 overlay->auto_adjust_size = g_value_get_boolean (value);
904 overlay->need_render = TRUE;
906 case PROP_VERTICAL_RENDER:
907 overlay->use_vertical_render = g_value_get_boolean (value);
908 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
909 gst_base_text_overlay_update_render_mode (overlay);
910 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
911 overlay->need_render = TRUE;
914 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
918 overlay->need_render = TRUE;
919 GST_OBJECT_UNLOCK (overlay);
923 gst_base_text_overlay_get_property (GObject * object, guint prop_id,
924 GValue * value, GParamSpec * pspec)
926 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
928 GST_OBJECT_LOCK (overlay);
931 g_value_set_string (value, overlay->default_text);
934 g_value_set_boolean (value, overlay->want_shading);
937 g_value_set_int (value, overlay->xpad);
940 g_value_set_int (value, overlay->ypad);
943 g_value_set_int (value, overlay->deltax);
946 g_value_set_int (value, overlay->deltay);
949 g_value_set_double (value, overlay->xpos);
952 g_value_set_double (value, overlay->ypos);
954 case PROP_VALIGNMENT:
955 g_value_set_enum (value, overlay->valign);
957 case PROP_HALIGNMENT:
958 g_value_set_enum (value, overlay->halign);
961 g_value_set_enum (value, overlay->wrap_mode);
964 g_value_set_boolean (value, overlay->silent);
966 case PROP_LINE_ALIGNMENT:
967 g_value_set_enum (value, overlay->line_align);
970 g_value_set_boolean (value, overlay->wait_text);
972 case PROP_AUTO_ADJUST_SIZE:
973 g_value_set_boolean (value, overlay->auto_adjust_size);
975 case PROP_VERTICAL_RENDER:
976 g_value_set_boolean (value, overlay->use_vertical_render);
979 g_value_set_uint (value, overlay->color);
981 case PROP_OUTLINE_COLOR:
982 g_value_set_uint (value, overlay->outline_color);
985 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
989 overlay->need_render = TRUE;
990 GST_OBJECT_UNLOCK (overlay);
994 gst_base_text_overlay_src_query (GstPad * pad, GstObject * parent,
997 gboolean ret = FALSE;
998 GstBaseTextOverlay *overlay = NULL;
1000 overlay = GST_BASE_TEXT_OVERLAY (parent);
1002 switch (GST_QUERY_TYPE (query)) {
1003 case GST_QUERY_CAPS:
1005 GstCaps *filter, *caps;
1007 gst_query_parse_caps (query, &filter);
1008 caps = gst_base_text_overlay_getcaps (pad, filter);
1009 gst_query_set_caps_result (query, caps);
1010 gst_caps_unref (caps);
1015 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1023 gst_base_text_overlay_src_event (GstPad * pad, GstObject * parent,
1026 gboolean ret = FALSE;
1027 GstBaseTextOverlay *overlay = NULL;
1029 overlay = GST_BASE_TEXT_OVERLAY (parent);
1031 switch (GST_EVENT_TYPE (event)) {
1032 case GST_EVENT_SEEK:{
1035 /* We don't handle seek if we have not text pad */
1036 if (!overlay->text_linked) {
1037 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1038 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1042 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1044 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1046 /* Flush downstream, only for flushing seek */
1047 if (flags & GST_SEEK_FLAG_FLUSH)
1048 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1050 /* Mark ourself as flushing, unblock chains */
1051 GST_OBJECT_LOCK (overlay);
1052 overlay->video_flushing = TRUE;
1053 overlay->text_flushing = TRUE;
1054 gst_base_text_overlay_pop_text (overlay);
1055 GST_OBJECT_UNLOCK (overlay);
1057 /* Seek on each sink pad */
1058 gst_event_ref (event);
1059 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1061 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1063 gst_event_unref (event);
1068 if (overlay->text_linked) {
1069 gst_event_ref (event);
1070 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1071 gst_pad_push_event (overlay->text_sinkpad, event);
1073 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1084 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1086 GstBaseTextOverlay *overlay;
1090 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1091 if (G_UNLIKELY (!overlay))
1092 return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1094 if (pad == overlay->srcpad)
1095 otherpad = overlay->video_sinkpad;
1097 otherpad = overlay->srcpad;
1099 /* we can do what the peer can */
1100 caps = gst_pad_peer_query_caps (otherpad, filter);
1102 GstCaps *temp, *templ;
1104 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1106 /* filtered against our padtemplate */
1107 templ = gst_pad_get_pad_template_caps (otherpad);
1108 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1109 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1110 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1111 gst_caps_unref (caps);
1112 gst_caps_unref (templ);
1113 /* this is what we can do */
1116 /* no peer, our padtemplate is enough then */
1117 caps = gst_pad_get_pad_template_caps (pad);
1119 GstCaps *intersection;
1122 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1123 gst_caps_unref (caps);
1124 caps = intersection;
1128 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1130 gst_object_unref (overlay);
1136 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1137 PangoFontDescription * desc)
1139 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1140 overlay->shadow_offset = (double) (font_size) / 13.0;
1141 overlay->outline_offset = (double) (font_size) / 15.0;
1142 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1143 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1146 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1147 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1148 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1149 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1153 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1154 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1161 gint width = overlay->image_width;
1162 gint height = overlay->image_height;
1168 if (xpos + width > overlay->width) {
1169 width = overlay->width - xpos;
1172 if (ypos + height > overlay->height) {
1173 height = overlay->height - ypos;
1176 dest += (ypos / 1) * dest_stride;
1178 for (i = 0; i < height; i++) {
1179 pimage = text_image + 4 * (i * overlay->image_width);
1180 py = dest + i * dest_stride + xpos;
1181 for (j = 0; j < width; j++) {
1182 b = pimage[CAIRO_ARGB_B];
1183 g = pimage[CAIRO_ARGB_G];
1184 r = pimage[CAIRO_ARGB_R];
1185 a = pimage[CAIRO_ARGB_A];
1186 CAIRO_UNPREMULTIPLY (a, r, g, b);
1193 COMP_Y (y, r, g, b);
1195 BLEND (*py++, a, y, x);
1201 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1202 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1203 guint destcb_stride, guint destcr_stride, guint pix_stride)
1208 gushort r1, g1, b1, a1;
1209 guchar *pimage1, *pimage2;
1211 gint width = overlay->image_width - 2;
1212 gint height = overlay->image_height - 2;
1220 if (xpos + width > overlay->width) {
1221 width = overlay->width - xpos;
1224 if (ypos + height > overlay->height) {
1225 height = overlay->height - ypos;
1228 destcb += (ypos / 2) * destcb_stride;
1229 destcr += (ypos / 2) * destcr_stride;
1231 for (i = 0; i < height; i += 2) {
1232 pimage1 = text_image + 4 * (i * overlay->image_width);
1233 pimage2 = pimage1 + 4 * overlay->image_width;
1234 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1235 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1236 for (j = 0; j < width; j += 2) {
1237 b = pimage1[CAIRO_ARGB_B];
1238 g = pimage1[CAIRO_ARGB_G];
1239 r = pimage1[CAIRO_ARGB_R];
1240 a = pimage1[CAIRO_ARGB_A];
1241 CAIRO_UNPREMULTIPLY (a, r, g, b);
1244 b1 = pimage1[CAIRO_ARGB_B];
1245 g1 = pimage1[CAIRO_ARGB_G];
1246 r1 = pimage1[CAIRO_ARGB_R];
1247 a1 = pimage1[CAIRO_ARGB_A];
1248 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1255 b1 = pimage2[CAIRO_ARGB_B];
1256 g1 = pimage2[CAIRO_ARGB_G];
1257 r1 = pimage2[CAIRO_ARGB_R];
1258 a1 = pimage2[CAIRO_ARGB_A];
1259 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1266 /* + 2 for rounding */
1267 b1 = pimage2[CAIRO_ARGB_B];
1268 g1 = pimage2[CAIRO_ARGB_G];
1269 r1 = pimage2[CAIRO_ARGB_R];
1270 a1 = pimage2[CAIRO_ARGB_A];
1271 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1288 COMP_U (cb, r, g, b);
1289 COMP_V (cr, r, g, b);
1292 BLEND (*pcb, a, cb, x);
1294 BLEND (*pcr, a, cr, x);
1303 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1304 const gchar * string, gint textlen)
1307 cairo_surface_t *surface;
1308 PangoRectangle ink_rect, logical_rect;
1309 cairo_matrix_t cairo_matrix;
1311 double scalef = 1.0;
1314 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1316 if (overlay->auto_adjust_size) {
1317 /* 640 pixel is default */
1318 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1320 pango_layout_set_width (overlay->layout, -1);
1321 /* set text on pango layout */
1322 pango_layout_set_markup (overlay->layout, string, textlen);
1324 /* get subtitle image size */
1325 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1327 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1329 if (width + overlay->deltax >
1330 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1332 * subtitle image width is larger then overlay width
1333 * so rearrange overlay wrap mode.
1335 gst_base_text_overlay_update_wrap_mode (overlay);
1336 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1337 width = overlay->width;
1341 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1342 if (height > overlay->height) {
1343 height = overlay->height;
1345 if (overlay->use_vertical_render) {
1346 PangoRectangle rect;
1347 PangoContext *context;
1348 PangoMatrix matrix = PANGO_MATRIX_INIT;
1351 context = pango_layout_get_context (overlay->layout);
1353 pango_matrix_rotate (&matrix, -90);
1355 rect.x = rect.y = 0;
1357 rect.height = height;
1358 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1359 matrix.x0 = -rect.x;
1360 matrix.y0 = -rect.y;
1362 pango_context_set_matrix (context, &matrix);
1364 cairo_matrix.xx = matrix.xx;
1365 cairo_matrix.yx = matrix.yx;
1366 cairo_matrix.xy = matrix.xy;
1367 cairo_matrix.yy = matrix.yy;
1368 cairo_matrix.x0 = matrix.x0;
1369 cairo_matrix.y0 = matrix.y0;
1370 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1376 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1379 /* reallocate surface */
1380 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1382 surface = cairo_image_surface_create_for_data (overlay->text_image,
1383 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1384 cr = cairo_create (surface);
1387 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1390 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1392 if (overlay->want_shading)
1393 cairo_paint_with_alpha (cr, overlay->shading_value);
1395 /* apply transformations */
1396 cairo_set_matrix (cr, &cairo_matrix);
1398 /* FIXME: We use show_layout everywhere except for the surface
1399 * because it's really faster and internally does all kinds of
1400 * caching. Unfortunately we have to paint to a cairo path for
1401 * the outline and this is slow. Once Pango supports user fonts
1402 * we should use them, see
1403 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1405 * Idea would the be, to create a cairo user font that
1406 * does shadow, outline, text painting in the
1407 * render_glyph function.
1410 /* draw shadow text */
1412 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1413 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1414 pango_cairo_show_layout (cr, overlay->layout);
1417 a = (overlay->outline_color >> 24) & 0xff;
1418 r = (overlay->outline_color >> 16) & 0xff;
1419 g = (overlay->outline_color >> 8) & 0xff;
1420 b = (overlay->outline_color >> 0) & 0xff;
1422 /* draw outline text */
1424 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1425 cairo_set_line_width (cr, overlay->outline_offset);
1426 pango_cairo_layout_path (cr, overlay->layout);
1430 a = (overlay->color >> 24) & 0xff;
1431 r = (overlay->color >> 16) & 0xff;
1432 g = (overlay->color >> 8) & 0xff;
1433 b = (overlay->color >> 0) & 0xff;
1437 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1438 pango_cairo_show_layout (cr, overlay->layout);
1442 cairo_surface_destroy (surface);
1443 overlay->image_width = width;
1444 overlay->image_height = height;
1445 overlay->baseline_y = ink_rect.y;
1446 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1453 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1454 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1456 gint i, j, dest_stride;
1459 dest_stride = dest->info.stride[0];
1460 dest_ptr = dest->data[0];
1462 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1463 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1465 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1466 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1468 for (i = y0; i < y1; ++i) {
1469 for (j = x0; j < x1; ++j) {
1470 gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
1472 dest_ptr[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1478 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1479 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1482 guint dest_stride, pixel_stride;
1485 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
1486 dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
1487 pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
1489 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1490 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1492 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1493 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1496 x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
1498 x1 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x1);
1501 y0 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y0);
1503 y1 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y1);
1505 for (i = y0; i < y1; i++) {
1506 for (j = x0; j < x1; j++) {
1510 y_pos = (i * dest_stride) + j * pixel_stride;
1511 y = dest_ptr[y_pos] + overlay->shading_value;
1513 dest_ptr[y_pos] = CLAMP (y, 0, 255);
1518 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1519 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1520 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1522 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
1523 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1528 dest_ptr = dest->data[0];
1530 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1531 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1533 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1534 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1536 for (i = y0; i < y1; i++) {
1537 for (j = x0; j < x1; j++) {
1540 y_pos = (i * 4 * overlay->width) + j * 4;
1541 for (k = 0; k < 4; k++) {
1542 y = dest_ptr[y_pos + k] + overlay->shading_value;
1543 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);
1549 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1550 static inline void \
1551 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
1552 gint x0, gint x1, gint y0, gint y1) \
1557 dest_ptr = dest->data[0];\
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++) {\
1568 y_pos = (i * 4 * overlay->width) + j * 4;\
1569 for (k = OFFSET; k < 3+OFFSET; k++) {\
1570 y = dest_ptr[y_pos + k] + overlay->shading_value;\
1571 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);\
1576 ARGB_SHADE_FUNCTION (ARGB, 1);
1577 ARGB_SHADE_FUNCTION (ABGR, 1);
1578 ARGB_SHADE_FUNCTION (RGBA, 0);
1579 ARGB_SHADE_FUNCTION (BGRA, 0);
1583 * - use proper strides and offset for I420
1584 * - don't draw over the edge of the picture (try a longer
1585 * text with a huge font size)
1589 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1590 GstVideoFrame * dest, gint xpos, gint ypos)
1592 int y_stride, u_stride, v_stride;
1593 guint8 *y_pixels, *u_pixels, *v_pixels;
1595 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1596 * to a boundary of integer number of U/V pixels:
1598 xpos = GST_ROUND_UP_2 (xpos);
1599 ypos = GST_ROUND_UP_2 (ypos);
1601 y_pixels = dest->data[0];
1602 u_pixels = dest->data[1];
1603 v_pixels = dest->data[2];
1604 y_stride = dest->info.stride[0];
1605 u_stride = dest->info.stride[1];
1606 v_stride = dest->info.stride[2];
1608 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1609 overlay->text_image, y_stride);
1610 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1611 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 2);
1615 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1616 GstVideoFrame * dest, gint xpos, gint ypos)
1618 int y_stride, u_stride, v_stride;
1619 guint8 *y_pixels, *u_pixels, *v_pixels;
1621 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1622 * to a boundary of integer number of U/V pixels:
1624 xpos = GST_ROUND_UP_2 (xpos);
1625 ypos = GST_ROUND_UP_2 (ypos);
1627 y_pixels = dest->data[0];
1628 u_pixels = dest->data[1];
1629 v_pixels = dest->data[2];
1630 y_stride = dest->info.stride[0];
1631 u_stride = dest->info.stride[1];
1632 v_stride = dest->info.stride[2];
1634 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1635 overlay->text_image, y_stride);
1636 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1637 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 1);
1641 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1642 GstVideoFrame * dest, gint xpos, gint ypos)
1649 guchar *pimage, *dest_ptr;
1652 yuv_pixels = dest->data[0];
1654 /* because U/V is 2x horizontally subsampled, we need to round to a
1655 * boundary of integer number of U/V pixels in x dimension:
1657 xpos = GST_ROUND_UP_2 (xpos);
1659 w = overlay->image_width - 2;
1660 h = overlay->image_height - 2;
1666 if (xpos + w > overlay->width) {
1667 w = overlay->width - xpos;
1670 if (ypos + h > overlay->height) {
1671 h = overlay->height - ypos;
1674 for (i = 0; i < h; i++) {
1675 pimage = overlay->text_image + i * overlay->image_width * 4;
1676 dest_ptr = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1677 for (j = 0; j < w; j += 2) {
1678 b0 = pimage[CAIRO_ARGB_B];
1679 g0 = pimage[CAIRO_ARGB_G];
1680 r0 = pimage[CAIRO_ARGB_R];
1681 a0 = pimage[CAIRO_ARGB_A];
1682 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1685 b1 = pimage[CAIRO_ARGB_B];
1686 g1 = pimage[CAIRO_ARGB_G];
1687 r1 = pimage[CAIRO_ARGB_R];
1688 a1 = pimage[CAIRO_ARGB_A];
1689 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1699 COMP_Y (y0, r0, g0, b0);
1700 COMP_Y (y1, r1, g1, b1);
1710 COMP_U (u, r0, g0, b0);
1711 COMP_V (v, r0, g0, b0);
1713 BLEND (*dest_ptr, a0, u, *dest_ptr);
1715 BLEND (*dest_ptr, a0, y0, *dest_ptr);
1717 BLEND (*dest_ptr, a0, v, *dest_ptr);
1719 BLEND (*dest_ptr, a0, y1, *dest_ptr);
1726 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1727 GstVideoFrame * dest, gint xpos, gint ypos)
1733 guchar *pimage, *dest_ptr;
1736 rgb_pixels = dest->data[0];
1738 w = overlay->image_width;
1739 h = overlay->image_height;
1745 if (xpos + w > overlay->width) {
1746 w = overlay->width - xpos;
1749 if (ypos + h > overlay->height) {
1750 h = overlay->height - ypos;
1753 for (i = 0; i < h; i++) {
1754 pimage = overlay->text_image + i * overlay->image_width * 4;
1755 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1756 for (j = 0; j < w; j++) {
1757 a = pimage[CAIRO_ARGB_A];
1758 b = pimage[CAIRO_ARGB_B];
1759 g = pimage[CAIRO_ARGB_G];
1760 r = pimage[CAIRO_ARGB_R];
1762 CAIRO_UNPREMULTIPLY (a, r, g, b);
1764 // convert background to yuv
1765 COMP_Y (y, r, g, b);
1766 COMP_U (u, r, g, b);
1767 COMP_V (v, r, g, b);
1769 // preform text "OVER" background alpha compositing
1770 a1 = a + (dest_ptr[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1771 OVER (dest_ptr[1], a, y, dest_ptr[0], dest_ptr[1], a1);
1772 OVER (dest_ptr[2], a, u, dest_ptr[0], dest_ptr[2], a1);
1773 OVER (dest_ptr[3], a, v, dest_ptr[0], dest_ptr[3], a1);
1774 dest_ptr[0] = a1 - 1; // remove the temporary 1 we added
1782 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1783 static inline void \
1784 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1785 GstVideoFrame * dest, gint xpos, gint ypos) \
1790 guchar *pimage, *dest_ptr; \
1791 guint8 *rgb_pixels;\
1793 rgb_pixels = dest->data[0];\
1795 w = overlay->image_width; \
1796 h = overlay->image_height; \
1802 if (xpos + w > overlay->width) { \
1803 w = overlay->width - xpos; \
1806 if (ypos + h > overlay->height) { \
1807 h = overlay->height - ypos; \
1810 for (i = 0; i < h; i++) { \
1811 pimage = overlay->text_image + i * overlay->image_width * 4; \
1812 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1813 for (j = 0; j < w; j++) { \
1814 a = pimage[CAIRO_ARGB_A]; \
1815 b = pimage[CAIRO_ARGB_B]; \
1816 g = pimage[CAIRO_ARGB_G]; \
1817 r = pimage[CAIRO_ARGB_R]; \
1818 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1819 b = (b*a + dest_ptr[B] * (255-a)) / 255; \
1820 g = (g*a + dest_ptr[G] * (255-a)) / 255; \
1821 r = (r*a + dest_ptr[R] * (255-a)) / 255; \
1831 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1832 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1833 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1834 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1836 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1837 static inline void \
1838 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1839 GstVideoFrame * dest, gint xpos, gint ypos) \
1841 int a, r, g, b, a1; \
1844 guchar *pimage, *dest_ptr; \
1845 guint8 *rgb_pixels;\
1847 rgb_pixels = dest->data[0];\
1849 w = overlay->image_width; \
1850 h = overlay->image_height; \
1856 if (xpos + w > overlay->width) { \
1857 w = overlay->width - xpos; \
1860 if (ypos + h > overlay->height) { \
1861 h = overlay->height - ypos; \
1864 for (i = 0; i < h; i++) { \
1865 pimage = overlay->text_image + i * overlay->image_width * 4; \
1866 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1867 for (j = 0; j < w; j++) { \
1868 a = pimage[CAIRO_ARGB_A]; \
1869 b = pimage[CAIRO_ARGB_B]; \
1870 g = pimage[CAIRO_ARGB_G]; \
1871 r = pimage[CAIRO_ARGB_R]; \
1872 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1873 a1 = a + (dest_ptr[A] * (255 - a)) / 255 + 1; \
1874 OVER (dest_ptr[R], a, r, dest_ptr[0], dest_ptr[R], a1); \
1875 OVER (dest_ptr[G], a, g, dest_ptr[0], dest_ptr[G], a1); \
1876 OVER (dest_ptr[B], a, b, dest_ptr[0], dest_ptr[B], a1); \
1877 dest_ptr[A] = a1 - 1; \
1883 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1884 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1885 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1886 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1889 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1890 const gchar * text, gint textlen)
1894 if (!overlay->need_render) {
1895 GST_DEBUG ("Using previously rendered text.");
1899 /* -1 is the whole string */
1900 if (text != NULL && textlen < 0) {
1901 textlen = strlen (text);
1905 string = g_strndup (text, textlen);
1906 } else { /* empty string */
1907 string = g_strdup (" ");
1909 g_strdelimit (string, "\r\t", ' ');
1910 textlen = strlen (string);
1912 /* FIXME: should we check for UTF-8 here? */
1914 GST_DEBUG ("Rendering '%s'", string);
1915 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1919 overlay->need_render = FALSE;
1922 static GstFlowReturn
1923 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1924 GstBuffer * video_frame)
1928 GstBaseTextOverlayVAlign valign;
1929 GstBaseTextOverlayHAlign halign;
1930 GstVideoFrame frame;
1932 width = overlay->image_width;
1933 height = overlay->image_height;
1935 video_frame = gst_buffer_make_writable (video_frame);
1937 if (!gst_video_frame_map (&frame, &overlay->info, video_frame, GST_MAP_WRITE))
1940 if (overlay->use_vertical_render)
1941 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1943 halign = overlay->halign;
1946 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1947 xpos = overlay->xpad;
1949 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1950 xpos = (overlay->width - width) / 2;
1952 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1953 xpos = overlay->width - width - overlay->xpad;
1955 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1956 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1957 xpos = CLAMP (xpos, 0, overlay->width - width);
1964 xpos += overlay->deltax;
1966 if (overlay->use_vertical_render)
1967 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1969 valign = overlay->valign;
1972 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1973 ypos = overlay->height - height - overlay->ypad;
1975 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1976 ypos = overlay->height - (height + overlay->ypad);
1978 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1979 ypos = overlay->ypad;
1981 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1982 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1983 ypos = CLAMP (ypos, 0, overlay->height - height);
1985 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1986 ypos = (overlay->height - height) / 2;
1989 ypos = overlay->ypad;
1992 ypos += overlay->deltay;
1994 /* shaded background box */
1995 if (overlay->want_shading) {
1996 switch (overlay->format) {
1997 case GST_VIDEO_FORMAT_I420:
1998 case GST_VIDEO_FORMAT_NV12:
1999 case GST_VIDEO_FORMAT_NV21:
2000 gst_base_text_overlay_shade_planar_Y (overlay, &frame,
2001 xpos, xpos + overlay->image_width,
2002 ypos, ypos + overlay->image_height);
2004 case GST_VIDEO_FORMAT_AYUV:
2005 case GST_VIDEO_FORMAT_UYVY:
2006 gst_base_text_overlay_shade_packed_Y (overlay, &frame,
2007 xpos, xpos + overlay->image_width,
2008 ypos, ypos + overlay->image_height);
2010 case GST_VIDEO_FORMAT_xRGB:
2011 gst_base_text_overlay_shade_xRGB (overlay, &frame,
2012 xpos, xpos + overlay->image_width,
2013 ypos, ypos + overlay->image_height);
2015 case GST_VIDEO_FORMAT_xBGR:
2016 gst_base_text_overlay_shade_xBGR (overlay, &frame,
2017 xpos, xpos + overlay->image_width,
2018 ypos, ypos + overlay->image_height);
2020 case GST_VIDEO_FORMAT_BGRx:
2021 gst_base_text_overlay_shade_BGRx (overlay, &frame,
2022 xpos, xpos + overlay->image_width,
2023 ypos, ypos + overlay->image_height);
2025 case GST_VIDEO_FORMAT_RGBx:
2026 gst_base_text_overlay_shade_RGBx (overlay, &frame,
2027 xpos, xpos + overlay->image_width,
2028 ypos, ypos + overlay->image_height);
2030 case GST_VIDEO_FORMAT_ARGB:
2031 gst_base_text_overlay_shade_ARGB (overlay, &frame,
2032 xpos, xpos + overlay->image_width,
2033 ypos, ypos + overlay->image_height);
2035 case GST_VIDEO_FORMAT_ABGR:
2036 gst_base_text_overlay_shade_ABGR (overlay, &frame,
2037 xpos, xpos + overlay->image_width,
2038 ypos, ypos + overlay->image_height);
2040 case GST_VIDEO_FORMAT_RGBA:
2041 gst_base_text_overlay_shade_RGBA (overlay, &frame,
2042 xpos, xpos + overlay->image_width,
2043 ypos, ypos + overlay->image_height);
2045 case GST_VIDEO_FORMAT_BGRA:
2046 gst_base_text_overlay_shade_BGRA (overlay, &frame,
2047 xpos, xpos + overlay->image_width,
2048 ypos, ypos + overlay->image_height);
2051 g_assert_not_reached ();
2058 if (overlay->text_image) {
2059 switch (overlay->format) {
2060 case GST_VIDEO_FORMAT_I420:
2061 gst_base_text_overlay_blit_I420 (overlay, &frame, xpos, ypos);
2063 case GST_VIDEO_FORMAT_NV12:
2064 case GST_VIDEO_FORMAT_NV21:
2065 gst_base_text_overlay_blit_NV12_NV21 (overlay, &frame, xpos, ypos);
2067 case GST_VIDEO_FORMAT_UYVY:
2068 gst_base_text_overlay_blit_UYVY (overlay, &frame, xpos, ypos);
2070 case GST_VIDEO_FORMAT_AYUV:
2071 gst_base_text_overlay_blit_AYUV (overlay, &frame, xpos, ypos);
2073 case GST_VIDEO_FORMAT_BGRx:
2074 gst_base_text_overlay_blit_BGRx (overlay, &frame, xpos, ypos);
2076 case GST_VIDEO_FORMAT_xRGB:
2077 gst_base_text_overlay_blit_xRGB (overlay, &frame, xpos, ypos);
2079 case GST_VIDEO_FORMAT_RGBx:
2080 gst_base_text_overlay_blit_RGBx (overlay, &frame, xpos, ypos);
2082 case GST_VIDEO_FORMAT_xBGR:
2083 gst_base_text_overlay_blit_xBGR (overlay, &frame, xpos, ypos);
2085 case GST_VIDEO_FORMAT_ARGB:
2086 gst_base_text_overlay_blit_ARGB (overlay, &frame, xpos, ypos);
2088 case GST_VIDEO_FORMAT_ABGR:
2089 gst_base_text_overlay_blit_ABGR (overlay, &frame, xpos, ypos);
2091 case GST_VIDEO_FORMAT_RGBA:
2092 gst_base_text_overlay_blit_RGBA (overlay, &frame, xpos, ypos);
2094 case GST_VIDEO_FORMAT_BGRA:
2095 gst_base_text_overlay_blit_BGRA (overlay, &frame, xpos, ypos);
2098 g_assert_not_reached ();
2101 gst_video_frame_unmap (&frame);
2103 return gst_pad_push (overlay->srcpad, video_frame);
2108 GST_DEBUG_OBJECT (overlay, "received invalid buffer");
2113 static GstPadLinkReturn
2114 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2116 GstBaseTextOverlay *overlay;
2118 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2119 if (G_UNLIKELY (!overlay))
2120 return GST_PAD_LINK_REFUSED;
2122 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2124 overlay->text_linked = TRUE;
2126 gst_object_unref (overlay);
2128 return GST_PAD_LINK_OK;
2132 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2134 GstBaseTextOverlay *overlay;
2136 /* don't use gst_pad_get_parent() here, will deadlock */
2137 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2139 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2141 overlay->text_linked = FALSE;
2143 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2147 gst_base_text_overlay_text_event (GstPad * pad, GstObject * parent,
2150 gboolean ret = FALSE;
2151 GstBaseTextOverlay *overlay = NULL;
2153 overlay = GST_BASE_TEXT_OVERLAY (parent);
2155 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2157 switch (GST_EVENT_TYPE (event)) {
2158 case GST_EVENT_CAPS:
2162 gst_event_parse_caps (event, &caps);
2163 ret = gst_base_text_overlay_setcaps_txt (overlay, caps);
2164 gst_event_unref (event);
2167 case GST_EVENT_SEGMENT:
2169 const GstSegment *segment;
2171 overlay->text_eos = FALSE;
2173 gst_event_parse_segment (event, &segment);
2175 if (segment->format == GST_FORMAT_TIME) {
2176 GST_OBJECT_LOCK (overlay);
2177 gst_segment_copy_into (segment, &overlay->text_segment);
2178 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2179 &overlay->text_segment);
2180 GST_OBJECT_UNLOCK (overlay);
2182 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2183 ("received non-TIME newsegment event on text input"));
2186 gst_event_unref (event);
2189 /* wake up the video chain, it might be waiting for a text buffer or
2190 * a text segment update */
2191 GST_OBJECT_LOCK (overlay);
2192 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2193 GST_OBJECT_UNLOCK (overlay);
2196 case GST_EVENT_FLUSH_STOP:
2197 GST_OBJECT_LOCK (overlay);
2198 GST_INFO_OBJECT (overlay, "text flush stop");
2199 overlay->text_flushing = FALSE;
2200 overlay->text_eos = FALSE;
2201 gst_base_text_overlay_pop_text (overlay);
2202 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2203 GST_OBJECT_UNLOCK (overlay);
2204 gst_event_unref (event);
2207 case GST_EVENT_FLUSH_START:
2208 GST_OBJECT_LOCK (overlay);
2209 GST_INFO_OBJECT (overlay, "text flush start");
2210 overlay->text_flushing = TRUE;
2211 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2212 GST_OBJECT_UNLOCK (overlay);
2213 gst_event_unref (event);
2217 GST_OBJECT_LOCK (overlay);
2218 overlay->text_eos = TRUE;
2219 GST_INFO_OBJECT (overlay, "text EOS");
2220 /* wake up the video chain, it might be waiting for a text buffer or
2221 * a text segment update */
2222 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2223 GST_OBJECT_UNLOCK (overlay);
2224 gst_event_unref (event);
2228 ret = gst_pad_event_default (pad, parent, event);
2236 gst_base_text_overlay_video_event (GstPad * pad, GstObject * parent,
2239 gboolean ret = FALSE;
2240 GstBaseTextOverlay *overlay = NULL;
2242 overlay = GST_BASE_TEXT_OVERLAY (parent);
2244 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2246 switch (GST_EVENT_TYPE (event)) {
2247 case GST_EVENT_CAPS:
2251 gst_event_parse_caps (event, &caps);
2252 ret = gst_base_text_overlay_setcaps (overlay, caps);
2253 gst_event_unref (event);
2256 case GST_EVENT_SEGMENT:
2258 const GstSegment *segment;
2260 GST_DEBUG_OBJECT (overlay, "received new segment");
2262 gst_event_parse_segment (event, &segment);
2264 if (segment->format == GST_FORMAT_TIME) {
2265 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2268 gst_segment_copy_into (segment, &overlay->segment);
2270 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2271 ("received non-TIME newsegment event on video input"));
2274 ret = gst_pad_event_default (pad, parent, event);
2278 GST_OBJECT_LOCK (overlay);
2279 GST_INFO_OBJECT (overlay, "video EOS");
2280 overlay->video_eos = TRUE;
2281 GST_OBJECT_UNLOCK (overlay);
2282 ret = gst_pad_event_default (pad, parent, event);
2284 case GST_EVENT_FLUSH_START:
2285 GST_OBJECT_LOCK (overlay);
2286 GST_INFO_OBJECT (overlay, "video flush start");
2287 overlay->video_flushing = TRUE;
2288 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2289 GST_OBJECT_UNLOCK (overlay);
2290 ret = gst_pad_event_default (pad, parent, event);
2292 case GST_EVENT_FLUSH_STOP:
2293 GST_OBJECT_LOCK (overlay);
2294 GST_INFO_OBJECT (overlay, "video flush stop");
2295 overlay->video_flushing = FALSE;
2296 overlay->video_eos = FALSE;
2297 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2298 GST_OBJECT_UNLOCK (overlay);
2299 ret = gst_pad_event_default (pad, parent, event);
2302 ret = gst_pad_event_default (pad, parent, event);
2310 gst_base_text_overlay_video_query (GstPad * pad, GstObject * parent,
2313 gboolean ret = FALSE;
2315 switch (GST_QUERY_TYPE (query)) {
2316 case GST_QUERY_CAPS:
2318 GstCaps *filter, *caps;
2320 gst_query_parse_caps (query, &filter);
2321 caps = gst_base_text_overlay_getcaps (pad, filter);
2322 gst_query_set_caps_result (query, caps);
2323 gst_caps_unref (caps);
2328 ret = gst_pad_query_default (pad, parent, query);
2335 /* Called with lock held */
2337 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2339 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2341 if (overlay->text_buffer) {
2342 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2343 overlay->text_buffer);
2344 gst_buffer_unref (overlay->text_buffer);
2345 overlay->text_buffer = NULL;
2348 /* Let the text task know we used that buffer */
2349 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2352 /* We receive text buffers here. If they are out of segment we just ignore them.
2353 If the buffer is in our segment we keep it internally except if another one
2354 is already waiting here, in that case we wait that it gets kicked out */
2355 static GstFlowReturn
2356 gst_base_text_overlay_text_chain (GstPad * pad, GstObject * parent,
2359 GstFlowReturn ret = GST_FLOW_OK;
2360 GstBaseTextOverlay *overlay = NULL;
2361 gboolean in_seg = FALSE;
2362 guint64 clip_start = 0, clip_stop = 0;
2364 overlay = GST_BASE_TEXT_OVERLAY (parent);
2366 GST_OBJECT_LOCK (overlay);
2368 if (overlay->text_flushing) {
2369 GST_OBJECT_UNLOCK (overlay);
2370 ret = GST_FLOW_FLUSHING;
2371 GST_LOG_OBJECT (overlay, "text flushing");
2375 if (overlay->text_eos) {
2376 GST_OBJECT_UNLOCK (overlay);
2378 GST_LOG_OBJECT (overlay, "text EOS");
2382 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2383 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2384 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2385 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2386 GST_BUFFER_DURATION (buffer)));
2388 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2391 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2392 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2394 stop = GST_CLOCK_TIME_NONE;
2396 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2397 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2403 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2404 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2405 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2406 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2408 /* Wait for the previous buffer to go away */
2409 while (overlay->text_buffer != NULL) {
2410 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2411 GST_DEBUG_PAD_NAME (pad));
2412 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2413 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2414 if (overlay->text_flushing) {
2415 GST_OBJECT_UNLOCK (overlay);
2416 ret = GST_FLOW_FLUSHING;
2421 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2422 overlay->text_segment.position = clip_start;
2424 overlay->text_buffer = buffer;
2425 /* That's a new text buffer we need to render */
2426 overlay->need_render = TRUE;
2428 /* in case the video chain is waiting for a text buffer, wake it up */
2429 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2432 GST_OBJECT_UNLOCK (overlay);
2439 static GstFlowReturn
2440 gst_base_text_overlay_video_chain (GstPad * pad, GstObject * parent,
2443 GstBaseTextOverlayClass *klass;
2444 GstBaseTextOverlay *overlay;
2445 GstFlowReturn ret = GST_FLOW_OK;
2446 gboolean in_seg = FALSE;
2447 guint64 start, stop, clip_start = 0, clip_stop = 0;
2450 overlay = GST_BASE_TEXT_OVERLAY (parent);
2451 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2453 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2454 goto missing_timestamp;
2456 /* ignore buffers that are outside of the current segment */
2457 start = GST_BUFFER_TIMESTAMP (buffer);
2459 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2460 stop = GST_CLOCK_TIME_NONE;
2462 stop = start + GST_BUFFER_DURATION (buffer);
2465 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2466 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2467 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2469 /* segment_clip() will adjust start unconditionally to segment_start if
2470 * no stop time is provided, so handle this ourselves */
2471 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2472 goto out_of_segment;
2474 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2475 &clip_start, &clip_stop);
2478 goto out_of_segment;
2480 /* if the buffer is only partially in the segment, fix up stamps */
2481 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2482 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2483 buffer = gst_buffer_make_writable (buffer);
2484 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2486 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2489 /* now, after we've done the clipping, fix up end time if there's no
2490 * duration (we only use those estimated values internally though, we
2491 * don't want to set bogus values on the buffer itself) */
2495 gint fps_num, fps_denom;
2497 /* FIXME, store this in setcaps */
2498 caps = gst_pad_get_current_caps (pad);
2499 s = gst_caps_get_structure (caps, 0);
2500 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2501 fps_num && fps_denom) {
2502 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2503 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2505 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2506 stop = start + 1; /* we need to assume some interval */
2508 gst_caps_unref (caps);
2511 gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2515 GST_OBJECT_LOCK (overlay);
2517 if (overlay->video_flushing)
2520 if (overlay->video_eos)
2523 if (overlay->silent) {
2524 GST_OBJECT_UNLOCK (overlay);
2525 ret = gst_pad_push (overlay->srcpad, buffer);
2527 /* Update position */
2528 overlay->segment.position = clip_start;
2533 /* Text pad not linked, rendering internal text */
2534 if (!overlay->text_linked) {
2535 if (klass->get_text) {
2536 text = klass->get_text (overlay, buffer);
2538 text = g_strdup (overlay->default_text);
2541 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2542 "text: '%s'", GST_STR_NULL (text));
2544 GST_OBJECT_UNLOCK (overlay);
2546 if (text != NULL && *text != '\0') {
2547 /* Render and push */
2548 gst_base_text_overlay_render_text (overlay, text, -1);
2549 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2551 /* Invalid or empty string */
2552 ret = gst_pad_push (overlay->srcpad, buffer);
2555 /* Text pad linked, check if we have a text buffer queued */
2556 if (overlay->text_buffer) {
2557 gboolean pop_text = FALSE, valid_text_time = TRUE;
2558 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2559 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2560 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2561 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2562 GstClockTime vid_running_time, vid_running_time_end;
2564 /* if the text buffer isn't stamped right, pop it off the
2565 * queue and display it for the current video frame only */
2566 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2567 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2568 GST_WARNING_OBJECT (overlay,
2569 "Got text buffer with invalid timestamp or duration");
2571 valid_text_time = FALSE;
2573 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2574 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2578 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2580 vid_running_time_end =
2581 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2584 /* If timestamp and duration are valid */
2585 if (valid_text_time) {
2587 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2589 text_running_time_end =
2590 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2594 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2595 GST_TIME_ARGS (text_running_time),
2596 GST_TIME_ARGS (text_running_time_end));
2597 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2598 GST_TIME_ARGS (vid_running_time),
2599 GST_TIME_ARGS (vid_running_time_end));
2601 /* Text too old or in the future */
2602 if (valid_text_time && text_running_time_end <= vid_running_time) {
2603 /* text buffer too old, get rid of it and do nothing */
2604 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2606 gst_base_text_overlay_pop_text (overlay);
2607 GST_OBJECT_UNLOCK (overlay);
2608 goto wait_for_text_buf;
2609 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2610 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2611 GST_OBJECT_UNLOCK (overlay);
2612 /* Push the video frame */
2613 ret = gst_pad_push (overlay->srcpad, buffer);
2619 gst_buffer_map (overlay->text_buffer, &map, GST_MAP_READ);
2620 in_text = (gchar *) map.data;
2623 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2624 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2625 * here on purpose, this is something that needs fixing upstream */
2626 if (!g_utf8_validate (in_text, in_size, NULL)) {
2627 const gchar *end = NULL;
2629 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2630 in_text = g_strndup (in_text, in_size);
2631 while (!g_utf8_validate (in_text, in_size, &end) && end)
2632 *((gchar *) end) = '*';
2635 /* Get the string */
2636 if (overlay->have_pango_markup) {
2637 text = g_strndup (in_text, in_size);
2639 text = g_markup_escape_text (in_text, in_size);
2642 if (text != NULL && *text != '\0') {
2643 gint text_len = strlen (text);
2645 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2646 text[text_len - 1] == '\r')) {
2649 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2650 gst_base_text_overlay_render_text (overlay, text, text_len);
2652 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2653 gst_base_text_overlay_render_text (overlay, " ", 1);
2655 if (in_text != (gchar *) map.data)
2658 gst_buffer_unmap (overlay->text_buffer, &map);
2660 GST_OBJECT_UNLOCK (overlay);
2661 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2663 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2664 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2669 GST_OBJECT_LOCK (overlay);
2670 gst_base_text_overlay_pop_text (overlay);
2671 GST_OBJECT_UNLOCK (overlay);
2674 gboolean wait_for_text_buf = TRUE;
2676 if (overlay->text_eos)
2677 wait_for_text_buf = FALSE;
2679 if (!overlay->wait_text)
2680 wait_for_text_buf = FALSE;
2682 /* Text pad linked, but no text buffer available - what now? */
2683 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2684 GstClockTime text_start_running_time, text_position_running_time;
2685 GstClockTime vid_running_time;
2688 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2689 GST_BUFFER_TIMESTAMP (buffer));
2690 text_start_running_time =
2691 gst_segment_to_running_time (&overlay->text_segment,
2692 GST_FORMAT_TIME, overlay->text_segment.start);
2693 text_position_running_time =
2694 gst_segment_to_running_time (&overlay->text_segment,
2695 GST_FORMAT_TIME, overlay->text_segment.position);
2697 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2698 vid_running_time < text_start_running_time) ||
2699 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2700 vid_running_time < text_position_running_time)) {
2701 wait_for_text_buf = FALSE;
2705 if (wait_for_text_buf) {
2706 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2707 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2708 GST_DEBUG_OBJECT (overlay, "resuming");
2709 GST_OBJECT_UNLOCK (overlay);
2710 goto wait_for_text_buf;
2712 GST_OBJECT_UNLOCK (overlay);
2713 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2714 ret = gst_pad_push (overlay->srcpad, buffer);
2721 /* Update position */
2722 overlay->segment.position = clip_start;
2728 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2729 gst_buffer_unref (buffer);
2735 GST_OBJECT_UNLOCK (overlay);
2736 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2737 gst_buffer_unref (buffer);
2738 return GST_FLOW_FLUSHING;
2742 GST_OBJECT_UNLOCK (overlay);
2743 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2744 gst_buffer_unref (buffer);
2745 return GST_FLOW_EOS;
2749 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2750 gst_buffer_unref (buffer);
2755 static GstStateChangeReturn
2756 gst_base_text_overlay_change_state (GstElement * element,
2757 GstStateChange transition)
2759 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2760 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2762 switch (transition) {
2763 case GST_STATE_CHANGE_PAUSED_TO_READY:
2764 GST_OBJECT_LOCK (overlay);
2765 overlay->text_flushing = TRUE;
2766 overlay->video_flushing = TRUE;
2767 /* pop_text will broadcast on the GCond and thus also make the video
2768 * chain exit if it's waiting for a text buffer */
2769 gst_base_text_overlay_pop_text (overlay);
2770 GST_OBJECT_UNLOCK (overlay);
2776 ret = parent_class->change_state (element, transition);
2777 if (ret == GST_STATE_CHANGE_FAILURE)
2780 switch (transition) {
2781 case GST_STATE_CHANGE_READY_TO_PAUSED:
2782 GST_OBJECT_LOCK (overlay);
2783 overlay->text_flushing = FALSE;
2784 overlay->video_flushing = FALSE;
2785 overlay->video_eos = FALSE;
2786 overlay->text_eos = FALSE;
2787 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2788 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2789 GST_OBJECT_UNLOCK (overlay);
2799 plugin_init (GstPlugin * plugin)
2801 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2802 GST_TYPE_TEXT_OVERLAY) ||
2803 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2804 GST_TYPE_TIME_OVERLAY) ||
2805 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2806 GST_TYPE_CLOCK_OVERLAY) ||
2807 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2808 GST_TYPE_TEXT_RENDER)) {
2812 /*texttestsrc_plugin_init(module, plugin); */
2814 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2819 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2820 "pango", "Pango-based text rendering and overlay", plugin_init,
2821 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)