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_mutex_new ();
423 klass->get_text = gst_base_text_overlay_get_text;
425 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
426 g_param_spec_string ("text", "text",
427 "Text to be display.", DEFAULT_PROP_TEXT,
428 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
429 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
430 g_param_spec_boolean ("shaded-background", "shaded background",
431 "Whether to shade the background under the text area",
432 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
433 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
434 g_param_spec_enum ("valignment", "vertical alignment",
435 "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
436 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
437 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
438 g_param_spec_enum ("halignment", "horizontal alignment",
439 "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
440 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
441 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
442 g_param_spec_string ("valign", "vertical alignment",
443 "Vertical alignment of the text (deprecated; use valignment)",
444 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
445 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
446 g_param_spec_string ("halign", "horizontal alignment",
447 "Horizontal alignment of the text (deprecated; use halignment)",
448 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
449 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
450 g_param_spec_int ("xpad", "horizontal paddding",
451 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
452 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
453 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
454 g_param_spec_int ("ypad", "vertical padding",
455 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
456 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
457 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
458 g_param_spec_int ("deltax", "X position modifier",
459 "Shift X position to the left or to the right. Unit is pixels.",
460 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
461 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
462 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
463 g_param_spec_int ("deltay", "Y position modifier",
464 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
465 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
467 * GstBaseTextOverlay:xpos
469 * Horizontal position of the rendered text when using positioned alignment.
473 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
474 g_param_spec_double ("xpos", "horizontal position",
475 "Horizontal position when using position alignment", 0, 1.0,
477 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
479 * GstBaseTextOverlay:ypos
481 * Vertical position of the rendered text when using positioned alignment.
485 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
486 g_param_spec_double ("ypos", "vertical position",
487 "Vertical position when using position alignment", 0, 1.0,
489 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
490 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
491 g_param_spec_enum ("wrap-mode", "wrap mode",
492 "Whether to wrap the text and if so how.",
493 GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
494 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
495 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
496 g_param_spec_string ("font-desc", "font description",
497 "Pango font description of font to be used for rendering. "
498 "See documentation of pango_font_description_from_string "
499 "for syntax.", DEFAULT_PROP_FONT_DESC,
500 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
502 * GstBaseTextOverlay:color
504 * Color of the rendered text.
508 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
509 g_param_spec_uint ("color", "Color",
510 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
512 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
514 * GstTextOverlay:outline-color
516 * Color of the outline of the rendered text.
520 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
521 g_param_spec_uint ("outline-color", "Text Outline Color",
522 "Color to use for outline the text (big-endian ARGB).", 0,
523 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
524 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
527 * GstBaseTextOverlay:line-alignment
529 * Alignment of text lines relative to each other (for multi-line text)
533 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
534 g_param_spec_enum ("line-alignment", "line alignment",
535 "Alignment of text lines relative to each other.",
536 GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
537 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
539 * GstBaseTextOverlay:silent
541 * If set, no text is rendered. Useful to switch off text rendering
542 * temporarily without removing the textoverlay element from the pipeline.
546 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
547 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
548 g_param_spec_boolean ("silent", "silent",
549 "Whether to render the text string",
551 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
553 * GstBaseTextOverlay:wait-text
555 * If set, the video will block until a subtitle is received on the text pad.
556 * If video and subtitles are sent in sync, like from the same demuxer, this
557 * property should be set.
561 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
562 g_param_spec_boolean ("wait-text", "Wait Text",
563 "Whether to wait for subtitles",
564 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
566 g_object_class_install_property (G_OBJECT_CLASS (klass),
567 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
568 "Automatically adjust font size to screen-size.",
569 DEFAULT_PROP_AUTO_ADJUST_SIZE,
570 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
572 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
573 g_param_spec_boolean ("vertical-render", "vertical render",
574 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
575 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
579 gst_base_text_overlay_finalize (GObject * object)
581 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
583 g_free (overlay->default_text);
585 if (overlay->text_image) {
586 g_free (overlay->text_image);
587 overlay->text_image = NULL;
590 if (overlay->layout) {
591 g_object_unref (overlay->layout);
592 overlay->layout = NULL;
595 if (overlay->text_buffer) {
596 gst_buffer_unref (overlay->text_buffer);
597 overlay->text_buffer = NULL;
601 g_cond_free (overlay->cond);
602 overlay->cond = NULL;
605 G_OBJECT_CLASS (parent_class)->finalize (object);
609 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
610 GstBaseTextOverlayClass * klass)
612 GstPadTemplate *template;
613 PangoFontDescription *desc;
616 template = gst_static_pad_template_get (&video_sink_template_factory);
617 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
618 gst_object_unref (template);
619 gst_pad_set_event_function (overlay->video_sinkpad,
620 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_event));
621 gst_pad_set_chain_function (overlay->video_sinkpad,
622 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
623 gst_pad_set_query_function (overlay->video_sinkpad,
624 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_query));
625 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
628 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
632 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
633 gst_object_unref (template);
635 gst_pad_set_event_function (overlay->text_sinkpad,
636 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
637 gst_pad_set_chain_function (overlay->text_sinkpad,
638 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
639 gst_pad_set_link_function (overlay->text_sinkpad,
640 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
641 gst_pad_set_unlink_function (overlay->text_sinkpad,
642 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
643 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
647 template = gst_static_pad_template_get (&src_template_factory);
648 overlay->srcpad = gst_pad_new_from_template (template, "src");
649 gst_object_unref (template);
650 gst_pad_set_event_function (overlay->srcpad,
651 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_event));
652 gst_pad_set_query_function (overlay->srcpad,
653 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_query));
654 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
656 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
657 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
659 pango_layout_new (GST_BASE_TEXT_OVERLAY_GET_CLASS
660 (overlay)->pango_context);
662 pango_context_get_font_description (GST_BASE_TEXT_OVERLAY_GET_CLASS
663 (overlay)->pango_context);
664 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
666 overlay->color = DEFAULT_PROP_COLOR;
667 overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
668 overlay->halign = DEFAULT_PROP_HALIGNMENT;
669 overlay->valign = DEFAULT_PROP_VALIGNMENT;
670 overlay->xpad = DEFAULT_PROP_XPAD;
671 overlay->ypad = DEFAULT_PROP_YPAD;
672 overlay->deltax = DEFAULT_PROP_DELTAX;
673 overlay->deltay = DEFAULT_PROP_DELTAY;
674 overlay->xpos = DEFAULT_PROP_XPOS;
675 overlay->ypos = DEFAULT_PROP_YPOS;
677 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
679 overlay->want_shading = DEFAULT_PROP_SHADING;
680 overlay->shading_value = DEFAULT_SHADING_VALUE;
681 overlay->silent = DEFAULT_PROP_SILENT;
682 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
683 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
685 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
686 overlay->need_render = TRUE;
687 overlay->text_image = NULL;
688 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
689 gst_base_text_overlay_update_render_mode (overlay);
691 overlay->text_buffer = NULL;
692 overlay->text_linked = FALSE;
693 overlay->cond = g_cond_new ();
694 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
695 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
699 gst_base_text_overlay_update_wrap_mode (GstBaseTextOverlay * overlay)
701 if (overlay->wrap_mode == GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE) {
702 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
703 pango_layout_set_width (overlay->layout, -1);
707 if (overlay->auto_adjust_size) {
708 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
709 if (overlay->use_vertical_render) {
710 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
714 (overlay->use_vertical_render ? overlay->height : overlay->width) *
718 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
719 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
720 pango_layout_set_width (overlay->layout, width);
721 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
726 gst_base_text_overlay_update_render_mode (GstBaseTextOverlay * overlay)
728 PangoMatrix matrix = PANGO_MATRIX_INIT;
729 PangoContext *context = pango_layout_get_context (overlay->layout);
731 if (overlay->use_vertical_render) {
732 pango_matrix_rotate (&matrix, -90);
733 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
734 pango_context_set_matrix (context, &matrix);
735 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
737 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
738 pango_context_set_matrix (context, &matrix);
739 pango_layout_set_alignment (overlay->layout, overlay->line_align);
744 gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay, GstCaps * caps)
746 GstStructure *structure;
748 structure = gst_caps_get_structure (caps, 0);
749 overlay->have_pango_markup =
750 gst_structure_has_name (structure, "text/x-pango-markup");
755 /* FIXME: upstream nego (e.g. when the video window is resized) */
758 gst_base_text_overlay_setcaps (GstBaseTextOverlay * overlay, GstCaps * caps)
761 gboolean ret = FALSE;
763 if (!gst_video_info_from_caps (&info, caps))
766 overlay->info = info;
767 overlay->format = GST_VIDEO_INFO_FORMAT (&info);
768 overlay->width = GST_VIDEO_INFO_WIDTH (&info);
769 overlay->height = GST_VIDEO_INFO_HEIGHT (&info);
771 ret = gst_pad_push_event (overlay->srcpad, gst_event_new_caps (caps));
774 GST_OBJECT_LOCK (overlay);
775 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
776 gst_base_text_overlay_update_wrap_mode (overlay);
777 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
778 GST_OBJECT_UNLOCK (overlay);
786 GST_DEBUG_OBJECT (overlay, "could not parse caps");
792 gst_base_text_overlay_set_property (GObject * object, guint prop_id,
793 const GValue * value, GParamSpec * pspec)
795 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
797 GST_OBJECT_LOCK (overlay);
800 g_free (overlay->default_text);
801 overlay->default_text = g_value_dup_string (value);
802 overlay->need_render = TRUE;
805 overlay->want_shading = g_value_get_boolean (value);
808 overlay->xpad = g_value_get_int (value);
811 overlay->ypad = g_value_get_int (value);
814 overlay->deltax = g_value_get_int (value);
817 overlay->deltay = g_value_get_int (value);
820 overlay->xpos = g_value_get_double (value);
823 overlay->ypos = g_value_get_double (value);
826 const gchar *s = g_value_get_string (value);
828 if (s && g_ascii_strcasecmp (s, "left") == 0)
829 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_LEFT;
830 else if (s && g_ascii_strcasecmp (s, "center") == 0)
831 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_CENTER;
832 else if (s && g_ascii_strcasecmp (s, "right") == 0)
833 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
835 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
840 const gchar *s = g_value_get_string (value);
842 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
843 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE;
844 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
845 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM;
846 else if (s && g_ascii_strcasecmp (s, "top") == 0)
847 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
849 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
853 case PROP_VALIGNMENT:
854 overlay->valign = g_value_get_enum (value);
856 case PROP_HALIGNMENT:
857 overlay->halign = g_value_get_enum (value);
860 overlay->wrap_mode = g_value_get_enum (value);
861 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
862 gst_base_text_overlay_update_wrap_mode (overlay);
863 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
867 PangoFontDescription *desc;
868 const gchar *fontdesc_str;
870 fontdesc_str = g_value_get_string (value);
871 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
872 desc = pango_font_description_from_string (fontdesc_str);
874 GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
875 pango_layout_set_font_description (overlay->layout, desc);
876 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
877 pango_font_description_free (desc);
879 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
882 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
886 overlay->color = g_value_get_uint (value);
888 case PROP_OUTLINE_COLOR:
889 overlay->outline_color = g_value_get_uint (value);
892 overlay->silent = g_value_get_boolean (value);
894 case PROP_LINE_ALIGNMENT:
895 overlay->line_align = g_value_get_enum (value);
896 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
897 pango_layout_set_alignment (overlay->layout,
898 (PangoAlignment) overlay->line_align);
899 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
902 overlay->wait_text = g_value_get_boolean (value);
904 case PROP_AUTO_ADJUST_SIZE:
905 overlay->auto_adjust_size = g_value_get_boolean (value);
906 overlay->need_render = TRUE;
908 case PROP_VERTICAL_RENDER:
909 overlay->use_vertical_render = g_value_get_boolean (value);
910 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
911 gst_base_text_overlay_update_render_mode (overlay);
912 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
913 overlay->need_render = TRUE;
916 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
920 overlay->need_render = TRUE;
921 GST_OBJECT_UNLOCK (overlay);
925 gst_base_text_overlay_get_property (GObject * object, guint prop_id,
926 GValue * value, GParamSpec * pspec)
928 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
930 GST_OBJECT_LOCK (overlay);
933 g_value_set_string (value, overlay->default_text);
936 g_value_set_boolean (value, overlay->want_shading);
939 g_value_set_int (value, overlay->xpad);
942 g_value_set_int (value, overlay->ypad);
945 g_value_set_int (value, overlay->deltax);
948 g_value_set_int (value, overlay->deltay);
951 g_value_set_double (value, overlay->xpos);
954 g_value_set_double (value, overlay->ypos);
956 case PROP_VALIGNMENT:
957 g_value_set_enum (value, overlay->valign);
959 case PROP_HALIGNMENT:
960 g_value_set_enum (value, overlay->halign);
963 g_value_set_enum (value, overlay->wrap_mode);
966 g_value_set_boolean (value, overlay->silent);
968 case PROP_LINE_ALIGNMENT:
969 g_value_set_enum (value, overlay->line_align);
972 g_value_set_boolean (value, overlay->wait_text);
974 case PROP_AUTO_ADJUST_SIZE:
975 g_value_set_boolean (value, overlay->auto_adjust_size);
977 case PROP_VERTICAL_RENDER:
978 g_value_set_boolean (value, overlay->use_vertical_render);
981 g_value_set_uint (value, overlay->color);
983 case PROP_OUTLINE_COLOR:
984 g_value_set_uint (value, overlay->outline_color);
987 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
991 overlay->need_render = TRUE;
992 GST_OBJECT_UNLOCK (overlay);
996 gst_base_text_overlay_src_query (GstPad * pad, GstObject * parent,
999 gboolean ret = FALSE;
1000 GstBaseTextOverlay *overlay = NULL;
1002 overlay = GST_BASE_TEXT_OVERLAY (parent);
1004 switch (GST_QUERY_TYPE (query)) {
1005 case GST_QUERY_CAPS:
1007 GstCaps *filter, *caps;
1009 gst_query_parse_caps (query, &filter);
1010 caps = gst_base_text_overlay_getcaps (pad, filter);
1011 gst_query_set_caps_result (query, caps);
1012 gst_caps_unref (caps);
1017 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1025 gst_base_text_overlay_src_event (GstPad * pad, GstObject * parent,
1028 gboolean ret = FALSE;
1029 GstBaseTextOverlay *overlay = NULL;
1031 overlay = GST_BASE_TEXT_OVERLAY (parent);
1033 switch (GST_EVENT_TYPE (event)) {
1034 case GST_EVENT_SEEK:{
1037 /* We don't handle seek if we have not text pad */
1038 if (!overlay->text_linked) {
1039 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1040 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1044 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1046 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1048 /* Flush downstream, only for flushing seek */
1049 if (flags & GST_SEEK_FLAG_FLUSH)
1050 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1052 /* Mark ourself as flushing, unblock chains */
1053 GST_OBJECT_LOCK (overlay);
1054 overlay->video_flushing = TRUE;
1055 overlay->text_flushing = TRUE;
1056 gst_base_text_overlay_pop_text (overlay);
1057 GST_OBJECT_UNLOCK (overlay);
1059 /* Seek on each sink pad */
1060 gst_event_ref (event);
1061 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1063 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1065 gst_event_unref (event);
1070 if (overlay->text_linked) {
1071 gst_event_ref (event);
1072 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1073 gst_pad_push_event (overlay->text_sinkpad, event);
1075 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1086 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1088 GstBaseTextOverlay *overlay;
1092 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1093 if (G_UNLIKELY (!overlay))
1094 return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1096 if (pad == overlay->srcpad)
1097 otherpad = overlay->video_sinkpad;
1099 otherpad = overlay->srcpad;
1101 /* we can do what the peer can */
1102 caps = gst_pad_peer_query_caps (otherpad, filter);
1104 GstCaps *temp, *templ;
1106 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1108 /* filtered against our padtemplate */
1109 templ = gst_pad_get_pad_template_caps (otherpad);
1110 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1111 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1112 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1113 gst_caps_unref (caps);
1114 gst_caps_unref (templ);
1115 /* this is what we can do */
1118 /* no peer, our padtemplate is enough then */
1119 caps = gst_pad_get_pad_template_caps (pad);
1121 GstCaps *intersection;
1124 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1125 gst_caps_unref (caps);
1126 caps = intersection;
1130 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1132 gst_object_unref (overlay);
1138 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1139 PangoFontDescription * desc)
1141 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1142 overlay->shadow_offset = (double) (font_size) / 13.0;
1143 overlay->outline_offset = (double) (font_size) / 15.0;
1144 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1145 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1148 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1149 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1150 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1151 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1155 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1156 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1163 gint width = overlay->image_width;
1164 gint height = overlay->image_height;
1170 if (xpos + width > overlay->width) {
1171 width = overlay->width - xpos;
1174 if (ypos + height > overlay->height) {
1175 height = overlay->height - ypos;
1178 dest += (ypos / 1) * dest_stride;
1180 for (i = 0; i < height; i++) {
1181 pimage = text_image + 4 * (i * overlay->image_width);
1182 py = dest + i * dest_stride + xpos;
1183 for (j = 0; j < width; j++) {
1184 b = pimage[CAIRO_ARGB_B];
1185 g = pimage[CAIRO_ARGB_G];
1186 r = pimage[CAIRO_ARGB_R];
1187 a = pimage[CAIRO_ARGB_A];
1188 CAIRO_UNPREMULTIPLY (a, r, g, b);
1195 COMP_Y (y, r, g, b);
1197 BLEND (*py++, a, y, x);
1203 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1204 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1205 guint destcb_stride, guint destcr_stride, guint pix_stride)
1210 gushort r1, g1, b1, a1;
1211 guchar *pimage1, *pimage2;
1213 gint width = overlay->image_width - 2;
1214 gint height = overlay->image_height - 2;
1222 if (xpos + width > overlay->width) {
1223 width = overlay->width - xpos;
1226 if (ypos + height > overlay->height) {
1227 height = overlay->height - ypos;
1230 destcb += (ypos / 2) * destcb_stride;
1231 destcr += (ypos / 2) * destcr_stride;
1233 for (i = 0; i < height; i += 2) {
1234 pimage1 = text_image + 4 * (i * overlay->image_width);
1235 pimage2 = pimage1 + 4 * overlay->image_width;
1236 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1237 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1238 for (j = 0; j < width; j += 2) {
1239 b = pimage1[CAIRO_ARGB_B];
1240 g = pimage1[CAIRO_ARGB_G];
1241 r = pimage1[CAIRO_ARGB_R];
1242 a = pimage1[CAIRO_ARGB_A];
1243 CAIRO_UNPREMULTIPLY (a, r, g, b);
1246 b1 = pimage1[CAIRO_ARGB_B];
1247 g1 = pimage1[CAIRO_ARGB_G];
1248 r1 = pimage1[CAIRO_ARGB_R];
1249 a1 = pimage1[CAIRO_ARGB_A];
1250 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1257 b1 = pimage2[CAIRO_ARGB_B];
1258 g1 = pimage2[CAIRO_ARGB_G];
1259 r1 = pimage2[CAIRO_ARGB_R];
1260 a1 = pimage2[CAIRO_ARGB_A];
1261 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1268 /* + 2 for rounding */
1269 b1 = pimage2[CAIRO_ARGB_B];
1270 g1 = pimage2[CAIRO_ARGB_G];
1271 r1 = pimage2[CAIRO_ARGB_R];
1272 a1 = pimage2[CAIRO_ARGB_A];
1273 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1290 COMP_U (cb, r, g, b);
1291 COMP_V (cr, r, g, b);
1294 BLEND (*pcb, a, cb, x);
1296 BLEND (*pcr, a, cr, x);
1305 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1306 const gchar * string, gint textlen)
1309 cairo_surface_t *surface;
1310 PangoRectangle ink_rect, logical_rect;
1311 cairo_matrix_t cairo_matrix;
1313 double scalef = 1.0;
1316 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1318 if (overlay->auto_adjust_size) {
1319 /* 640 pixel is default */
1320 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1322 pango_layout_set_width (overlay->layout, -1);
1323 /* set text on pango layout */
1324 pango_layout_set_markup (overlay->layout, string, textlen);
1326 /* get subtitle image size */
1327 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1329 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1331 if (width + overlay->deltax >
1332 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1334 * subtitle image width is larger then overlay width
1335 * so rearrange overlay wrap mode.
1337 gst_base_text_overlay_update_wrap_mode (overlay);
1338 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1339 width = overlay->width;
1343 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1344 if (height > overlay->height) {
1345 height = overlay->height;
1347 if (overlay->use_vertical_render) {
1348 PangoRectangle rect;
1349 PangoContext *context;
1350 PangoMatrix matrix = PANGO_MATRIX_INIT;
1353 context = pango_layout_get_context (overlay->layout);
1355 pango_matrix_rotate (&matrix, -90);
1357 rect.x = rect.y = 0;
1359 rect.height = height;
1360 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1361 matrix.x0 = -rect.x;
1362 matrix.y0 = -rect.y;
1364 pango_context_set_matrix (context, &matrix);
1366 cairo_matrix.xx = matrix.xx;
1367 cairo_matrix.yx = matrix.yx;
1368 cairo_matrix.xy = matrix.xy;
1369 cairo_matrix.yy = matrix.yy;
1370 cairo_matrix.x0 = matrix.x0;
1371 cairo_matrix.y0 = matrix.y0;
1372 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1378 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1381 /* reallocate surface */
1382 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1384 surface = cairo_image_surface_create_for_data (overlay->text_image,
1385 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1386 cr = cairo_create (surface);
1389 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1392 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1394 if (overlay->want_shading)
1395 cairo_paint_with_alpha (cr, overlay->shading_value);
1397 /* apply transformations */
1398 cairo_set_matrix (cr, &cairo_matrix);
1400 /* FIXME: We use show_layout everywhere except for the surface
1401 * because it's really faster and internally does all kinds of
1402 * caching. Unfortunately we have to paint to a cairo path for
1403 * the outline and this is slow. Once Pango supports user fonts
1404 * we should use them, see
1405 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1407 * Idea would the be, to create a cairo user font that
1408 * does shadow, outline, text painting in the
1409 * render_glyph function.
1412 /* draw shadow text */
1414 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1415 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1416 pango_cairo_show_layout (cr, overlay->layout);
1419 a = (overlay->outline_color >> 24) & 0xff;
1420 r = (overlay->outline_color >> 16) & 0xff;
1421 g = (overlay->outline_color >> 8) & 0xff;
1422 b = (overlay->outline_color >> 0) & 0xff;
1424 /* draw outline text */
1426 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1427 cairo_set_line_width (cr, overlay->outline_offset);
1428 pango_cairo_layout_path (cr, overlay->layout);
1432 a = (overlay->color >> 24) & 0xff;
1433 r = (overlay->color >> 16) & 0xff;
1434 g = (overlay->color >> 8) & 0xff;
1435 b = (overlay->color >> 0) & 0xff;
1439 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1440 pango_cairo_show_layout (cr, overlay->layout);
1444 cairo_surface_destroy (surface);
1445 overlay->image_width = width;
1446 overlay->image_height = height;
1447 overlay->baseline_y = ink_rect.y;
1448 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1455 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1456 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1458 gint i, j, dest_stride;
1461 dest_stride = dest->info.stride[0];
1462 dest_ptr = dest->data[0];
1464 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1465 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1467 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1468 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1470 for (i = y0; i < y1; ++i) {
1471 for (j = x0; j < x1; ++j) {
1472 gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
1474 dest_ptr[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1480 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1481 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1484 guint dest_stride, pixel_stride;
1487 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
1488 dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
1489 pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
1491 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1492 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1494 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1495 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1498 x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
1500 x1 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x1);
1503 y0 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y0);
1505 y1 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y1);
1507 for (i = y0; i < y1; i++) {
1508 for (j = x0; j < x1; j++) {
1512 y_pos = (i * dest_stride) + j * pixel_stride;
1513 y = dest_ptr[y_pos] + overlay->shading_value;
1515 dest_ptr[y_pos] = CLAMP (y, 0, 255);
1520 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1521 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1522 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1524 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
1525 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1530 dest_ptr = dest->data[0];
1532 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1533 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1535 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1536 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1538 for (i = y0; i < y1; i++) {
1539 for (j = x0; j < x1; j++) {
1542 y_pos = (i * 4 * overlay->width) + j * 4;
1543 for (k = 0; k < 4; k++) {
1544 y = dest_ptr[y_pos + k] + overlay->shading_value;
1545 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);
1551 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1552 static inline void \
1553 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
1554 gint x0, gint x1, gint y0, gint y1) \
1559 dest_ptr = dest->data[0];\
1561 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1562 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1564 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1565 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1567 for (i = y0; i < y1; i++) {\
1568 for (j = x0; j < x1; j++) {\
1570 y_pos = (i * 4 * overlay->width) + j * 4;\
1571 for (k = OFFSET; k < 3+OFFSET; k++) {\
1572 y = dest_ptr[y_pos + k] + overlay->shading_value;\
1573 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);\
1578 ARGB_SHADE_FUNCTION (ARGB, 1);
1579 ARGB_SHADE_FUNCTION (ABGR, 1);
1580 ARGB_SHADE_FUNCTION (RGBA, 0);
1581 ARGB_SHADE_FUNCTION (BGRA, 0);
1585 * - use proper strides and offset for I420
1586 * - don't draw over the edge of the picture (try a longer
1587 * text with a huge font size)
1591 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1592 GstVideoFrame * dest, gint xpos, gint ypos)
1594 int y_stride, u_stride, v_stride;
1595 guint8 *y_pixels, *u_pixels, *v_pixels;
1597 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1598 * to a boundary of integer number of U/V pixels:
1600 xpos = GST_ROUND_UP_2 (xpos);
1601 ypos = GST_ROUND_UP_2 (ypos);
1603 y_pixels = dest->data[0];
1604 u_pixels = dest->data[1];
1605 v_pixels = dest->data[2];
1606 y_stride = dest->info.stride[0];
1607 u_stride = dest->info.stride[1];
1608 v_stride = dest->info.stride[2];
1610 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1611 overlay->text_image, y_stride);
1612 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1613 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 2);
1617 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1618 GstVideoFrame * dest, gint xpos, gint ypos)
1620 int y_stride, u_stride, v_stride;
1621 guint8 *y_pixels, *u_pixels, *v_pixels;
1623 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1624 * to a boundary of integer number of U/V pixels:
1626 xpos = GST_ROUND_UP_2 (xpos);
1627 ypos = GST_ROUND_UP_2 (ypos);
1629 y_pixels = dest->data[0];
1630 u_pixels = dest->data[1];
1631 v_pixels = dest->data[2];
1632 y_stride = dest->info.stride[0];
1633 u_stride = dest->info.stride[1];
1634 v_stride = dest->info.stride[2];
1636 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1637 overlay->text_image, y_stride);
1638 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1639 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 1);
1643 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1644 GstVideoFrame * dest, gint xpos, gint ypos)
1651 guchar *pimage, *dest_ptr;
1654 yuv_pixels = dest->data[0];
1656 /* because U/V is 2x horizontally subsampled, we need to round to a
1657 * boundary of integer number of U/V pixels in x dimension:
1659 xpos = GST_ROUND_UP_2 (xpos);
1661 w = overlay->image_width - 2;
1662 h = overlay->image_height - 2;
1668 if (xpos + w > overlay->width) {
1669 w = overlay->width - xpos;
1672 if (ypos + h > overlay->height) {
1673 h = overlay->height - ypos;
1676 for (i = 0; i < h; i++) {
1677 pimage = overlay->text_image + i * overlay->image_width * 4;
1678 dest_ptr = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1679 for (j = 0; j < w; j += 2) {
1680 b0 = pimage[CAIRO_ARGB_B];
1681 g0 = pimage[CAIRO_ARGB_G];
1682 r0 = pimage[CAIRO_ARGB_R];
1683 a0 = pimage[CAIRO_ARGB_A];
1684 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1687 b1 = pimage[CAIRO_ARGB_B];
1688 g1 = pimage[CAIRO_ARGB_G];
1689 r1 = pimage[CAIRO_ARGB_R];
1690 a1 = pimage[CAIRO_ARGB_A];
1691 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1701 COMP_Y (y0, r0, g0, b0);
1702 COMP_Y (y1, r1, g1, b1);
1712 COMP_U (u, r0, g0, b0);
1713 COMP_V (v, r0, g0, b0);
1715 BLEND (*dest_ptr, a0, u, *dest_ptr);
1717 BLEND (*dest_ptr, a0, y0, *dest_ptr);
1719 BLEND (*dest_ptr, a0, v, *dest_ptr);
1721 BLEND (*dest_ptr, a0, y1, *dest_ptr);
1728 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1729 GstVideoFrame * dest, gint xpos, gint ypos)
1735 guchar *pimage, *dest_ptr;
1738 rgb_pixels = dest->data[0];
1740 w = overlay->image_width;
1741 h = overlay->image_height;
1747 if (xpos + w > overlay->width) {
1748 w = overlay->width - xpos;
1751 if (ypos + h > overlay->height) {
1752 h = overlay->height - ypos;
1755 for (i = 0; i < h; i++) {
1756 pimage = overlay->text_image + i * overlay->image_width * 4;
1757 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1758 for (j = 0; j < w; j++) {
1759 a = pimage[CAIRO_ARGB_A];
1760 b = pimage[CAIRO_ARGB_B];
1761 g = pimage[CAIRO_ARGB_G];
1762 r = pimage[CAIRO_ARGB_R];
1764 CAIRO_UNPREMULTIPLY (a, r, g, b);
1766 // convert background to yuv
1767 COMP_Y (y, r, g, b);
1768 COMP_U (u, r, g, b);
1769 COMP_V (v, r, g, b);
1771 // preform text "OVER" background alpha compositing
1772 a1 = a + (dest_ptr[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1773 OVER (dest_ptr[1], a, y, dest_ptr[0], dest_ptr[1], a1);
1774 OVER (dest_ptr[2], a, u, dest_ptr[0], dest_ptr[2], a1);
1775 OVER (dest_ptr[3], a, v, dest_ptr[0], dest_ptr[3], a1);
1776 dest_ptr[0] = a1 - 1; // remove the temporary 1 we added
1784 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1785 static inline void \
1786 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1787 GstVideoFrame * dest, gint xpos, gint ypos) \
1792 guchar *pimage, *dest_ptr; \
1793 guint8 *rgb_pixels;\
1795 rgb_pixels = dest->data[0];\
1797 w = overlay->image_width; \
1798 h = overlay->image_height; \
1804 if (xpos + w > overlay->width) { \
1805 w = overlay->width - xpos; \
1808 if (ypos + h > overlay->height) { \
1809 h = overlay->height - ypos; \
1812 for (i = 0; i < h; i++) { \
1813 pimage = overlay->text_image + i * overlay->image_width * 4; \
1814 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1815 for (j = 0; j < w; j++) { \
1816 a = pimage[CAIRO_ARGB_A]; \
1817 b = pimage[CAIRO_ARGB_B]; \
1818 g = pimage[CAIRO_ARGB_G]; \
1819 r = pimage[CAIRO_ARGB_R]; \
1820 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1821 b = (b*a + dest_ptr[B] * (255-a)) / 255; \
1822 g = (g*a + dest_ptr[G] * (255-a)) / 255; \
1823 r = (r*a + dest_ptr[R] * (255-a)) / 255; \
1833 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1834 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1835 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1836 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1838 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1839 static inline void \
1840 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1841 GstVideoFrame * dest, gint xpos, gint ypos) \
1843 int a, r, g, b, a1; \
1846 guchar *pimage, *dest_ptr; \
1847 guint8 *rgb_pixels;\
1849 rgb_pixels = dest->data[0];\
1851 w = overlay->image_width; \
1852 h = overlay->image_height; \
1858 if (xpos + w > overlay->width) { \
1859 w = overlay->width - xpos; \
1862 if (ypos + h > overlay->height) { \
1863 h = overlay->height - ypos; \
1866 for (i = 0; i < h; i++) { \
1867 pimage = overlay->text_image + i * overlay->image_width * 4; \
1868 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1869 for (j = 0; j < w; j++) { \
1870 a = pimage[CAIRO_ARGB_A]; \
1871 b = pimage[CAIRO_ARGB_B]; \
1872 g = pimage[CAIRO_ARGB_G]; \
1873 r = pimage[CAIRO_ARGB_R]; \
1874 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1875 a1 = a + (dest_ptr[A] * (255 - a)) / 255 + 1; \
1876 OVER (dest_ptr[R], a, r, dest_ptr[0], dest_ptr[R], a1); \
1877 OVER (dest_ptr[G], a, g, dest_ptr[0], dest_ptr[G], a1); \
1878 OVER (dest_ptr[B], a, b, dest_ptr[0], dest_ptr[B], a1); \
1879 dest_ptr[A] = a1 - 1; \
1885 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1886 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1887 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1888 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1891 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1892 const gchar * text, gint textlen)
1896 if (!overlay->need_render) {
1897 GST_DEBUG ("Using previously rendered text.");
1901 /* -1 is the whole string */
1902 if (text != NULL && textlen < 0) {
1903 textlen = strlen (text);
1907 string = g_strndup (text, textlen);
1908 } else { /* empty string */
1909 string = g_strdup (" ");
1911 g_strdelimit (string, "\r\t", ' ');
1912 textlen = strlen (string);
1914 /* FIXME: should we check for UTF-8 here? */
1916 GST_DEBUG ("Rendering '%s'", string);
1917 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1921 overlay->need_render = FALSE;
1924 static GstFlowReturn
1925 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1926 GstBuffer * video_frame)
1930 GstBaseTextOverlayVAlign valign;
1931 GstBaseTextOverlayHAlign halign;
1932 GstVideoFrame frame;
1934 width = overlay->image_width;
1935 height = overlay->image_height;
1937 video_frame = gst_buffer_make_writable (video_frame);
1939 if (!gst_video_frame_map (&frame, &overlay->info, video_frame, GST_MAP_WRITE))
1942 if (overlay->use_vertical_render)
1943 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1945 halign = overlay->halign;
1948 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1949 xpos = overlay->xpad;
1951 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1952 xpos = (overlay->width - width) / 2;
1954 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1955 xpos = overlay->width - width - overlay->xpad;
1957 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1958 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1959 xpos = CLAMP (xpos, 0, overlay->width - width);
1966 xpos += overlay->deltax;
1968 if (overlay->use_vertical_render)
1969 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1971 valign = overlay->valign;
1974 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1975 ypos = overlay->height - height - overlay->ypad;
1977 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1978 ypos = overlay->height - (height + overlay->ypad);
1980 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1981 ypos = overlay->ypad;
1983 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1984 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1985 ypos = CLAMP (ypos, 0, overlay->height - height);
1987 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1988 ypos = (overlay->height - height) / 2;
1991 ypos = overlay->ypad;
1994 ypos += overlay->deltay;
1996 /* shaded background box */
1997 if (overlay->want_shading) {
1998 switch (overlay->format) {
1999 case GST_VIDEO_FORMAT_I420:
2000 case GST_VIDEO_FORMAT_NV12:
2001 case GST_VIDEO_FORMAT_NV21:
2002 gst_base_text_overlay_shade_planar_Y (overlay, &frame,
2003 xpos, xpos + overlay->image_width,
2004 ypos, ypos + overlay->image_height);
2006 case GST_VIDEO_FORMAT_AYUV:
2007 case GST_VIDEO_FORMAT_UYVY:
2008 gst_base_text_overlay_shade_packed_Y (overlay, &frame,
2009 xpos, xpos + overlay->image_width,
2010 ypos, ypos + overlay->image_height);
2012 case GST_VIDEO_FORMAT_xRGB:
2013 gst_base_text_overlay_shade_xRGB (overlay, &frame,
2014 xpos, xpos + overlay->image_width,
2015 ypos, ypos + overlay->image_height);
2017 case GST_VIDEO_FORMAT_xBGR:
2018 gst_base_text_overlay_shade_xBGR (overlay, &frame,
2019 xpos, xpos + overlay->image_width,
2020 ypos, ypos + overlay->image_height);
2022 case GST_VIDEO_FORMAT_BGRx:
2023 gst_base_text_overlay_shade_BGRx (overlay, &frame,
2024 xpos, xpos + overlay->image_width,
2025 ypos, ypos + overlay->image_height);
2027 case GST_VIDEO_FORMAT_RGBx:
2028 gst_base_text_overlay_shade_RGBx (overlay, &frame,
2029 xpos, xpos + overlay->image_width,
2030 ypos, ypos + overlay->image_height);
2032 case GST_VIDEO_FORMAT_ARGB:
2033 gst_base_text_overlay_shade_ARGB (overlay, &frame,
2034 xpos, xpos + overlay->image_width,
2035 ypos, ypos + overlay->image_height);
2037 case GST_VIDEO_FORMAT_ABGR:
2038 gst_base_text_overlay_shade_ABGR (overlay, &frame,
2039 xpos, xpos + overlay->image_width,
2040 ypos, ypos + overlay->image_height);
2042 case GST_VIDEO_FORMAT_RGBA:
2043 gst_base_text_overlay_shade_RGBA (overlay, &frame,
2044 xpos, xpos + overlay->image_width,
2045 ypos, ypos + overlay->image_height);
2047 case GST_VIDEO_FORMAT_BGRA:
2048 gst_base_text_overlay_shade_BGRA (overlay, &frame,
2049 xpos, xpos + overlay->image_width,
2050 ypos, ypos + overlay->image_height);
2053 g_assert_not_reached ();
2060 if (overlay->text_image) {
2061 switch (overlay->format) {
2062 case GST_VIDEO_FORMAT_I420:
2063 gst_base_text_overlay_blit_I420 (overlay, &frame, xpos, ypos);
2065 case GST_VIDEO_FORMAT_NV12:
2066 case GST_VIDEO_FORMAT_NV21:
2067 gst_base_text_overlay_blit_NV12_NV21 (overlay, &frame, xpos, ypos);
2069 case GST_VIDEO_FORMAT_UYVY:
2070 gst_base_text_overlay_blit_UYVY (overlay, &frame, xpos, ypos);
2072 case GST_VIDEO_FORMAT_AYUV:
2073 gst_base_text_overlay_blit_AYUV (overlay, &frame, xpos, ypos);
2075 case GST_VIDEO_FORMAT_BGRx:
2076 gst_base_text_overlay_blit_BGRx (overlay, &frame, xpos, ypos);
2078 case GST_VIDEO_FORMAT_xRGB:
2079 gst_base_text_overlay_blit_xRGB (overlay, &frame, xpos, ypos);
2081 case GST_VIDEO_FORMAT_RGBx:
2082 gst_base_text_overlay_blit_RGBx (overlay, &frame, xpos, ypos);
2084 case GST_VIDEO_FORMAT_xBGR:
2085 gst_base_text_overlay_blit_xBGR (overlay, &frame, xpos, ypos);
2087 case GST_VIDEO_FORMAT_ARGB:
2088 gst_base_text_overlay_blit_ARGB (overlay, &frame, xpos, ypos);
2090 case GST_VIDEO_FORMAT_ABGR:
2091 gst_base_text_overlay_blit_ABGR (overlay, &frame, xpos, ypos);
2093 case GST_VIDEO_FORMAT_RGBA:
2094 gst_base_text_overlay_blit_RGBA (overlay, &frame, xpos, ypos);
2096 case GST_VIDEO_FORMAT_BGRA:
2097 gst_base_text_overlay_blit_BGRA (overlay, &frame, xpos, ypos);
2100 g_assert_not_reached ();
2103 gst_video_frame_unmap (&frame);
2105 return gst_pad_push (overlay->srcpad, video_frame);
2110 GST_DEBUG_OBJECT (overlay, "received invalid buffer");
2115 static GstPadLinkReturn
2116 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2118 GstBaseTextOverlay *overlay;
2120 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2121 if (G_UNLIKELY (!overlay))
2122 return GST_PAD_LINK_REFUSED;
2124 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2126 overlay->text_linked = TRUE;
2128 gst_object_unref (overlay);
2130 return GST_PAD_LINK_OK;
2134 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2136 GstBaseTextOverlay *overlay;
2138 /* don't use gst_pad_get_parent() here, will deadlock */
2139 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2141 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2143 overlay->text_linked = FALSE;
2145 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2149 gst_base_text_overlay_text_event (GstPad * pad, GstObject * parent,
2152 gboolean ret = FALSE;
2153 GstBaseTextOverlay *overlay = NULL;
2155 overlay = GST_BASE_TEXT_OVERLAY (parent);
2157 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2159 switch (GST_EVENT_TYPE (event)) {
2160 case GST_EVENT_CAPS:
2164 gst_event_parse_caps (event, &caps);
2165 ret = gst_base_text_overlay_setcaps_txt (overlay, caps);
2166 gst_event_unref (event);
2169 case GST_EVENT_SEGMENT:
2171 const GstSegment *segment;
2173 overlay->text_eos = FALSE;
2175 gst_event_parse_segment (event, &segment);
2177 if (segment->format == GST_FORMAT_TIME) {
2178 GST_OBJECT_LOCK (overlay);
2179 gst_segment_copy_into (segment, &overlay->text_segment);
2180 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2181 &overlay->text_segment);
2182 GST_OBJECT_UNLOCK (overlay);
2184 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2185 ("received non-TIME newsegment event on text input"));
2188 gst_event_unref (event);
2191 /* wake up the video chain, it might be waiting for a text buffer or
2192 * a text segment update */
2193 GST_OBJECT_LOCK (overlay);
2194 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2195 GST_OBJECT_UNLOCK (overlay);
2198 case GST_EVENT_FLUSH_STOP:
2199 GST_OBJECT_LOCK (overlay);
2200 GST_INFO_OBJECT (overlay, "text flush stop");
2201 overlay->text_flushing = FALSE;
2202 overlay->text_eos = FALSE;
2203 gst_base_text_overlay_pop_text (overlay);
2204 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2205 GST_OBJECT_UNLOCK (overlay);
2206 gst_event_unref (event);
2209 case GST_EVENT_FLUSH_START:
2210 GST_OBJECT_LOCK (overlay);
2211 GST_INFO_OBJECT (overlay, "text flush start");
2212 overlay->text_flushing = TRUE;
2213 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2214 GST_OBJECT_UNLOCK (overlay);
2215 gst_event_unref (event);
2219 GST_OBJECT_LOCK (overlay);
2220 overlay->text_eos = TRUE;
2221 GST_INFO_OBJECT (overlay, "text EOS");
2222 /* wake up the video chain, it might be waiting for a text buffer or
2223 * a text segment update */
2224 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2225 GST_OBJECT_UNLOCK (overlay);
2226 gst_event_unref (event);
2230 ret = gst_pad_event_default (pad, parent, event);
2238 gst_base_text_overlay_video_event (GstPad * pad, GstObject * parent,
2241 gboolean ret = FALSE;
2242 GstBaseTextOverlay *overlay = NULL;
2244 overlay = GST_BASE_TEXT_OVERLAY (parent);
2246 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2248 switch (GST_EVENT_TYPE (event)) {
2249 case GST_EVENT_CAPS:
2253 gst_event_parse_caps (event, &caps);
2254 ret = gst_base_text_overlay_setcaps (overlay, caps);
2255 gst_event_unref (event);
2258 case GST_EVENT_SEGMENT:
2260 const GstSegment *segment;
2262 GST_DEBUG_OBJECT (overlay, "received new segment");
2264 gst_event_parse_segment (event, &segment);
2266 if (segment->format == GST_FORMAT_TIME) {
2267 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2270 gst_segment_copy_into (segment, &overlay->segment);
2272 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2273 ("received non-TIME newsegment event on video input"));
2276 ret = gst_pad_event_default (pad, parent, event);
2280 GST_OBJECT_LOCK (overlay);
2281 GST_INFO_OBJECT (overlay, "video EOS");
2282 overlay->video_eos = TRUE;
2283 GST_OBJECT_UNLOCK (overlay);
2284 ret = gst_pad_event_default (pad, parent, event);
2286 case GST_EVENT_FLUSH_START:
2287 GST_OBJECT_LOCK (overlay);
2288 GST_INFO_OBJECT (overlay, "video flush start");
2289 overlay->video_flushing = TRUE;
2290 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2291 GST_OBJECT_UNLOCK (overlay);
2292 ret = gst_pad_event_default (pad, parent, event);
2294 case GST_EVENT_FLUSH_STOP:
2295 GST_OBJECT_LOCK (overlay);
2296 GST_INFO_OBJECT (overlay, "video flush stop");
2297 overlay->video_flushing = FALSE;
2298 overlay->video_eos = FALSE;
2299 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2300 GST_OBJECT_UNLOCK (overlay);
2301 ret = gst_pad_event_default (pad, parent, event);
2304 ret = gst_pad_event_default (pad, parent, event);
2312 gst_base_text_overlay_video_query (GstPad * pad, GstObject * parent,
2315 gboolean ret = FALSE;
2317 switch (GST_QUERY_TYPE (query)) {
2318 case GST_QUERY_CAPS:
2320 GstCaps *filter, *caps;
2322 gst_query_parse_caps (query, &filter);
2323 caps = gst_base_text_overlay_getcaps (pad, filter);
2324 gst_query_set_caps_result (query, caps);
2325 gst_caps_unref (caps);
2330 ret = gst_pad_query_default (pad, parent, query);
2337 /* Called with lock held */
2339 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2341 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2343 if (overlay->text_buffer) {
2344 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2345 overlay->text_buffer);
2346 gst_buffer_unref (overlay->text_buffer);
2347 overlay->text_buffer = NULL;
2350 /* Let the text task know we used that buffer */
2351 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2354 /* We receive text buffers here. If they are out of segment we just ignore them.
2355 If the buffer is in our segment we keep it internally except if another one
2356 is already waiting here, in that case we wait that it gets kicked out */
2357 static GstFlowReturn
2358 gst_base_text_overlay_text_chain (GstPad * pad, GstObject * parent,
2361 GstFlowReturn ret = GST_FLOW_OK;
2362 GstBaseTextOverlay *overlay = NULL;
2363 gboolean in_seg = FALSE;
2364 guint64 clip_start = 0, clip_stop = 0;
2366 overlay = GST_BASE_TEXT_OVERLAY (parent);
2368 GST_OBJECT_LOCK (overlay);
2370 if (overlay->text_flushing) {
2371 GST_OBJECT_UNLOCK (overlay);
2372 ret = GST_FLOW_WRONG_STATE;
2373 GST_LOG_OBJECT (overlay, "text flushing");
2377 if (overlay->text_eos) {
2378 GST_OBJECT_UNLOCK (overlay);
2380 GST_LOG_OBJECT (overlay, "text EOS");
2384 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2385 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2386 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2387 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2388 GST_BUFFER_DURATION (buffer)));
2390 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2393 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2394 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2396 stop = GST_CLOCK_TIME_NONE;
2398 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2399 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2405 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2406 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2407 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2408 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2410 /* Wait for the previous buffer to go away */
2411 while (overlay->text_buffer != NULL) {
2412 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2413 GST_DEBUG_PAD_NAME (pad));
2414 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2415 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2416 if (overlay->text_flushing) {
2417 GST_OBJECT_UNLOCK (overlay);
2418 ret = GST_FLOW_WRONG_STATE;
2423 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2424 overlay->text_segment.position = clip_start;
2426 overlay->text_buffer = buffer;
2427 /* That's a new text buffer we need to render */
2428 overlay->need_render = TRUE;
2430 /* in case the video chain is waiting for a text buffer, wake it up */
2431 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2434 GST_OBJECT_UNLOCK (overlay);
2441 static GstFlowReturn
2442 gst_base_text_overlay_video_chain (GstPad * pad, GstObject * parent,
2445 GstBaseTextOverlayClass *klass;
2446 GstBaseTextOverlay *overlay;
2447 GstFlowReturn ret = GST_FLOW_OK;
2448 gboolean in_seg = FALSE;
2449 guint64 start, stop, clip_start = 0, clip_stop = 0;
2452 overlay = GST_BASE_TEXT_OVERLAY (parent);
2453 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2455 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2456 goto missing_timestamp;
2458 /* ignore buffers that are outside of the current segment */
2459 start = GST_BUFFER_TIMESTAMP (buffer);
2461 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2462 stop = GST_CLOCK_TIME_NONE;
2464 stop = start + GST_BUFFER_DURATION (buffer);
2467 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2468 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2469 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2471 /* segment_clip() will adjust start unconditionally to segment_start if
2472 * no stop time is provided, so handle this ourselves */
2473 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2474 goto out_of_segment;
2476 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2477 &clip_start, &clip_stop);
2480 goto out_of_segment;
2482 /* if the buffer is only partially in the segment, fix up stamps */
2483 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2484 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2485 buffer = gst_buffer_make_writable (buffer);
2486 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2488 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2491 /* now, after we've done the clipping, fix up end time if there's no
2492 * duration (we only use those estimated values internally though, we
2493 * don't want to set bogus values on the buffer itself) */
2497 gint fps_num, fps_denom;
2499 /* FIXME, store this in setcaps */
2500 caps = gst_pad_get_current_caps (pad);
2501 s = gst_caps_get_structure (caps, 0);
2502 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2503 fps_num && fps_denom) {
2504 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2505 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2507 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2508 stop = start + 1; /* we need to assume some interval */
2510 gst_caps_unref (caps);
2513 gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2517 GST_OBJECT_LOCK (overlay);
2519 if (overlay->video_flushing)
2522 if (overlay->video_eos)
2525 if (overlay->silent) {
2526 GST_OBJECT_UNLOCK (overlay);
2527 ret = gst_pad_push (overlay->srcpad, buffer);
2529 /* Update position */
2530 overlay->segment.position = clip_start;
2535 /* Text pad not linked, rendering internal text */
2536 if (!overlay->text_linked) {
2537 if (klass->get_text) {
2538 text = klass->get_text (overlay, buffer);
2540 text = g_strdup (overlay->default_text);
2543 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2544 "text: '%s'", GST_STR_NULL (text));
2546 GST_OBJECT_UNLOCK (overlay);
2548 if (text != NULL && *text != '\0') {
2549 /* Render and push */
2550 gst_base_text_overlay_render_text (overlay, text, -1);
2551 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2553 /* Invalid or empty string */
2554 ret = gst_pad_push (overlay->srcpad, buffer);
2557 /* Text pad linked, check if we have a text buffer queued */
2558 if (overlay->text_buffer) {
2559 gboolean pop_text = FALSE, valid_text_time = TRUE;
2560 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2561 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2562 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2563 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2564 GstClockTime vid_running_time, vid_running_time_end;
2566 /* if the text buffer isn't stamped right, pop it off the
2567 * queue and display it for the current video frame only */
2568 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2569 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2570 GST_WARNING_OBJECT (overlay,
2571 "Got text buffer with invalid timestamp or duration");
2573 valid_text_time = FALSE;
2575 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2576 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2580 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2582 vid_running_time_end =
2583 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2586 /* If timestamp and duration are valid */
2587 if (valid_text_time) {
2589 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2591 text_running_time_end =
2592 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2596 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2597 GST_TIME_ARGS (text_running_time),
2598 GST_TIME_ARGS (text_running_time_end));
2599 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2600 GST_TIME_ARGS (vid_running_time),
2601 GST_TIME_ARGS (vid_running_time_end));
2603 /* Text too old or in the future */
2604 if (valid_text_time && text_running_time_end <= vid_running_time) {
2605 /* text buffer too old, get rid of it and do nothing */
2606 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2608 gst_base_text_overlay_pop_text (overlay);
2609 GST_OBJECT_UNLOCK (overlay);
2610 goto wait_for_text_buf;
2611 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2612 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2613 GST_OBJECT_UNLOCK (overlay);
2614 /* Push the video frame */
2615 ret = gst_pad_push (overlay->srcpad, buffer);
2617 gchar *in_text, *otext;
2618 gsize in_size, osize;
2621 gst_buffer_map (overlay->text_buffer, &osize, NULL, GST_MAP_READ);
2625 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2626 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2627 * here on purpose, this is something that needs fixing upstream */
2628 if (!g_utf8_validate (in_text, in_size, NULL)) {
2629 const gchar *end = NULL;
2631 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2632 in_text = g_strndup (in_text, in_size);
2633 while (!g_utf8_validate (in_text, in_size, &end) && end)
2634 *((gchar *) end) = '*';
2637 /* Get the string */
2638 if (overlay->have_pango_markup) {
2639 text = g_strndup (in_text, in_size);
2641 text = g_markup_escape_text (in_text, in_size);
2644 if (text != NULL && *text != '\0') {
2645 gint text_len = strlen (text);
2647 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2648 text[text_len - 1] == '\r')) {
2651 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2652 gst_base_text_overlay_render_text (overlay, text, text_len);
2654 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2655 gst_base_text_overlay_render_text (overlay, " ", 1);
2657 gst_buffer_unmap (overlay->text_buffer, otext, osize);
2659 if (in_text != otext)
2662 GST_OBJECT_UNLOCK (overlay);
2663 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2665 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2666 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2671 GST_OBJECT_LOCK (overlay);
2672 gst_base_text_overlay_pop_text (overlay);
2673 GST_OBJECT_UNLOCK (overlay);
2676 gboolean wait_for_text_buf = TRUE;
2678 if (overlay->text_eos)
2679 wait_for_text_buf = FALSE;
2681 if (!overlay->wait_text)
2682 wait_for_text_buf = FALSE;
2684 /* Text pad linked, but no text buffer available - what now? */
2685 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2686 GstClockTime text_start_running_time, text_position_running_time;
2687 GstClockTime vid_running_time;
2690 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2691 GST_BUFFER_TIMESTAMP (buffer));
2692 text_start_running_time =
2693 gst_segment_to_running_time (&overlay->text_segment,
2694 GST_FORMAT_TIME, overlay->text_segment.start);
2695 text_position_running_time =
2696 gst_segment_to_running_time (&overlay->text_segment,
2697 GST_FORMAT_TIME, overlay->text_segment.position);
2699 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2700 vid_running_time < text_start_running_time) ||
2701 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2702 vid_running_time < text_position_running_time)) {
2703 wait_for_text_buf = FALSE;
2707 if (wait_for_text_buf) {
2708 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2709 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2710 GST_DEBUG_OBJECT (overlay, "resuming");
2711 GST_OBJECT_UNLOCK (overlay);
2712 goto wait_for_text_buf;
2714 GST_OBJECT_UNLOCK (overlay);
2715 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2716 ret = gst_pad_push (overlay->srcpad, buffer);
2723 /* Update position */
2724 overlay->segment.position = clip_start;
2730 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2731 gst_buffer_unref (buffer);
2737 GST_OBJECT_UNLOCK (overlay);
2738 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2739 gst_buffer_unref (buffer);
2740 return GST_FLOW_WRONG_STATE;
2744 GST_OBJECT_UNLOCK (overlay);
2745 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2746 gst_buffer_unref (buffer);
2747 return GST_FLOW_EOS;
2751 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2752 gst_buffer_unref (buffer);
2757 static GstStateChangeReturn
2758 gst_base_text_overlay_change_state (GstElement * element,
2759 GstStateChange transition)
2761 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2762 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2764 switch (transition) {
2765 case GST_STATE_CHANGE_PAUSED_TO_READY:
2766 GST_OBJECT_LOCK (overlay);
2767 overlay->text_flushing = TRUE;
2768 overlay->video_flushing = TRUE;
2769 /* pop_text will broadcast on the GCond and thus also make the video
2770 * chain exit if it's waiting for a text buffer */
2771 gst_base_text_overlay_pop_text (overlay);
2772 GST_OBJECT_UNLOCK (overlay);
2778 ret = parent_class->change_state (element, transition);
2779 if (ret == GST_STATE_CHANGE_FAILURE)
2782 switch (transition) {
2783 case GST_STATE_CHANGE_READY_TO_PAUSED:
2784 GST_OBJECT_LOCK (overlay);
2785 overlay->text_flushing = FALSE;
2786 overlay->video_flushing = FALSE;
2787 overlay->video_eos = FALSE;
2788 overlay->text_eos = FALSE;
2789 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2790 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2791 GST_OBJECT_UNLOCK (overlay);
2801 plugin_init (GstPlugin * plugin)
2803 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2804 GST_TYPE_TEXT_OVERLAY) ||
2805 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2806 GST_TYPE_TIME_OVERLAY) ||
2807 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2808 GST_TYPE_CLOCK_OVERLAY) ||
2809 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2810 GST_TYPE_TEXT_RENDER)) {
2814 /*texttestsrc_plugin_init(module, plugin); */
2816 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2821 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2822 "pango", "Pango-based text rendering and overlay", plugin_init,
2823 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)