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_font_map_create_context (PANGO_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,
738 (PangoAlignment) overlay->line_align);
743 gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay, GstCaps * caps)
745 GstStructure *structure;
747 structure = gst_caps_get_structure (caps, 0);
748 overlay->have_pango_markup =
749 gst_structure_has_name (structure, "text/x-pango-markup");
754 /* FIXME: upstream nego (e.g. when the video window is resized) */
757 gst_base_text_overlay_setcaps (GstBaseTextOverlay * overlay, GstCaps * caps)
760 gboolean ret = FALSE;
762 if (!gst_video_info_from_caps (&info, caps))
765 overlay->info = info;
766 overlay->format = GST_VIDEO_INFO_FORMAT (&info);
767 overlay->width = GST_VIDEO_INFO_WIDTH (&info);
768 overlay->height = GST_VIDEO_INFO_HEIGHT (&info);
770 ret = gst_pad_push_event (overlay->srcpad, gst_event_new_caps (caps));
773 GST_OBJECT_LOCK (overlay);
774 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
775 gst_base_text_overlay_update_wrap_mode (overlay);
776 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
777 GST_OBJECT_UNLOCK (overlay);
785 GST_DEBUG_OBJECT (overlay, "could not parse caps");
791 gst_base_text_overlay_set_property (GObject * object, guint prop_id,
792 const GValue * value, GParamSpec * pspec)
794 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
796 GST_OBJECT_LOCK (overlay);
799 g_free (overlay->default_text);
800 overlay->default_text = g_value_dup_string (value);
801 overlay->need_render = TRUE;
804 overlay->want_shading = g_value_get_boolean (value);
807 overlay->xpad = g_value_get_int (value);
810 overlay->ypad = g_value_get_int (value);
813 overlay->deltax = g_value_get_int (value);
816 overlay->deltay = g_value_get_int (value);
819 overlay->xpos = g_value_get_double (value);
822 overlay->ypos = g_value_get_double (value);
825 const gchar *s = g_value_get_string (value);
827 if (s && g_ascii_strcasecmp (s, "left") == 0)
828 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_LEFT;
829 else if (s && g_ascii_strcasecmp (s, "center") == 0)
830 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_CENTER;
831 else if (s && g_ascii_strcasecmp (s, "right") == 0)
832 overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
834 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
839 const gchar *s = g_value_get_string (value);
841 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
842 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE;
843 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
844 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM;
845 else if (s && g_ascii_strcasecmp (s, "top") == 0)
846 overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
848 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
852 case PROP_VALIGNMENT:
853 overlay->valign = g_value_get_enum (value);
855 case PROP_HALIGNMENT:
856 overlay->halign = g_value_get_enum (value);
859 overlay->wrap_mode = g_value_get_enum (value);
860 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
861 gst_base_text_overlay_update_wrap_mode (overlay);
862 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
866 PangoFontDescription *desc;
867 const gchar *fontdesc_str;
869 fontdesc_str = g_value_get_string (value);
870 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
871 desc = pango_font_description_from_string (fontdesc_str);
873 GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
874 pango_layout_set_font_description (overlay->layout, desc);
875 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
876 pango_font_description_free (desc);
878 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
881 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
885 overlay->color = g_value_get_uint (value);
887 case PROP_OUTLINE_COLOR:
888 overlay->outline_color = g_value_get_uint (value);
891 overlay->silent = g_value_get_boolean (value);
893 case PROP_LINE_ALIGNMENT:
894 overlay->line_align = g_value_get_enum (value);
895 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
896 pango_layout_set_alignment (overlay->layout,
897 (PangoAlignment) overlay->line_align);
898 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
901 overlay->wait_text = g_value_get_boolean (value);
903 case PROP_AUTO_ADJUST_SIZE:
904 overlay->auto_adjust_size = g_value_get_boolean (value);
905 overlay->need_render = TRUE;
907 case PROP_VERTICAL_RENDER:
908 overlay->use_vertical_render = g_value_get_boolean (value);
909 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
910 gst_base_text_overlay_update_render_mode (overlay);
911 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
912 overlay->need_render = TRUE;
915 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
919 overlay->need_render = TRUE;
920 GST_OBJECT_UNLOCK (overlay);
924 gst_base_text_overlay_get_property (GObject * object, guint prop_id,
925 GValue * value, GParamSpec * pspec)
927 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
929 GST_OBJECT_LOCK (overlay);
932 g_value_set_string (value, overlay->default_text);
935 g_value_set_boolean (value, overlay->want_shading);
938 g_value_set_int (value, overlay->xpad);
941 g_value_set_int (value, overlay->ypad);
944 g_value_set_int (value, overlay->deltax);
947 g_value_set_int (value, overlay->deltay);
950 g_value_set_double (value, overlay->xpos);
953 g_value_set_double (value, overlay->ypos);
955 case PROP_VALIGNMENT:
956 g_value_set_enum (value, overlay->valign);
958 case PROP_HALIGNMENT:
959 g_value_set_enum (value, overlay->halign);
962 g_value_set_enum (value, overlay->wrap_mode);
965 g_value_set_boolean (value, overlay->silent);
967 case PROP_LINE_ALIGNMENT:
968 g_value_set_enum (value, overlay->line_align);
971 g_value_set_boolean (value, overlay->wait_text);
973 case PROP_AUTO_ADJUST_SIZE:
974 g_value_set_boolean (value, overlay->auto_adjust_size);
976 case PROP_VERTICAL_RENDER:
977 g_value_set_boolean (value, overlay->use_vertical_render);
980 g_value_set_uint (value, overlay->color);
982 case PROP_OUTLINE_COLOR:
983 g_value_set_uint (value, overlay->outline_color);
986 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
990 overlay->need_render = TRUE;
991 GST_OBJECT_UNLOCK (overlay);
995 gst_base_text_overlay_src_query (GstPad * pad, GstObject * parent,
998 gboolean ret = FALSE;
999 GstBaseTextOverlay *overlay = NULL;
1001 overlay = GST_BASE_TEXT_OVERLAY (parent);
1003 switch (GST_QUERY_TYPE (query)) {
1004 case GST_QUERY_CAPS:
1006 GstCaps *filter, *caps;
1008 gst_query_parse_caps (query, &filter);
1009 caps = gst_base_text_overlay_getcaps (pad, filter);
1010 gst_query_set_caps_result (query, caps);
1011 gst_caps_unref (caps);
1016 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
1024 gst_base_text_overlay_src_event (GstPad * pad, GstObject * parent,
1027 gboolean ret = FALSE;
1028 GstBaseTextOverlay *overlay = NULL;
1030 overlay = GST_BASE_TEXT_OVERLAY (parent);
1032 switch (GST_EVENT_TYPE (event)) {
1033 case GST_EVENT_SEEK:{
1036 /* We don't handle seek if we have not text pad */
1037 if (!overlay->text_linked) {
1038 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1039 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1043 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1045 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1047 /* Flush downstream, only for flushing seek */
1048 if (flags & GST_SEEK_FLAG_FLUSH)
1049 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1051 /* Mark ourself as flushing, unblock chains */
1052 GST_OBJECT_LOCK (overlay);
1053 overlay->video_flushing = TRUE;
1054 overlay->text_flushing = TRUE;
1055 gst_base_text_overlay_pop_text (overlay);
1056 GST_OBJECT_UNLOCK (overlay);
1058 /* Seek on each sink pad */
1059 gst_event_ref (event);
1060 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1062 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1064 gst_event_unref (event);
1069 if (overlay->text_linked) {
1070 gst_event_ref (event);
1071 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1072 gst_pad_push_event (overlay->text_sinkpad, event);
1074 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1085 gst_base_text_overlay_getcaps (GstPad * pad, GstCaps * filter)
1087 GstBaseTextOverlay *overlay;
1091 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
1092 if (G_UNLIKELY (!overlay))
1093 return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1095 if (pad == overlay->srcpad)
1096 otherpad = overlay->video_sinkpad;
1098 otherpad = overlay->srcpad;
1100 /* we can do what the peer can */
1101 caps = gst_pad_peer_query_caps (otherpad, filter);
1103 GstCaps *temp, *templ;
1105 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1107 /* filtered against our padtemplate */
1108 templ = gst_pad_get_pad_template_caps (otherpad);
1109 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1110 temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
1111 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1112 gst_caps_unref (caps);
1113 gst_caps_unref (templ);
1114 /* this is what we can do */
1117 /* no peer, our padtemplate is enough then */
1118 caps = gst_pad_get_pad_template_caps (pad);
1120 GstCaps *intersection;
1123 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1124 gst_caps_unref (caps);
1125 caps = intersection;
1129 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1131 gst_object_unref (overlay);
1137 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1138 PangoFontDescription * desc)
1140 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1141 overlay->shadow_offset = (double) (font_size) / 13.0;
1142 overlay->outline_offset = (double) (font_size) / 15.0;
1143 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1144 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1147 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1148 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1149 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1150 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1154 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1155 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1162 gint width = overlay->image_width;
1163 gint height = overlay->image_height;
1169 if (xpos + width > overlay->width) {
1170 width = overlay->width - xpos;
1173 if (ypos + height > overlay->height) {
1174 height = overlay->height - ypos;
1177 dest += (ypos / 1) * dest_stride;
1179 for (i = 0; i < height; i++) {
1180 pimage = text_image + 4 * (i * overlay->image_width);
1181 py = dest + i * dest_stride + xpos;
1182 for (j = 0; j < width; j++) {
1183 b = pimage[CAIRO_ARGB_B];
1184 g = pimage[CAIRO_ARGB_G];
1185 r = pimage[CAIRO_ARGB_R];
1186 a = pimage[CAIRO_ARGB_A];
1187 CAIRO_UNPREMULTIPLY (a, r, g, b);
1194 COMP_Y (y, r, g, b);
1196 BLEND (*py++, a, y, x);
1202 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1203 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1204 guint destcb_stride, guint destcr_stride, guint pix_stride)
1209 gushort r1, g1, b1, a1;
1210 guchar *pimage1, *pimage2;
1212 gint width = overlay->image_width - 2;
1213 gint height = overlay->image_height - 2;
1221 if (xpos + width > overlay->width) {
1222 width = overlay->width - xpos;
1225 if (ypos + height > overlay->height) {
1226 height = overlay->height - ypos;
1229 destcb += (ypos / 2) * destcb_stride;
1230 destcr += (ypos / 2) * destcr_stride;
1232 for (i = 0; i < height; i += 2) {
1233 pimage1 = text_image + 4 * (i * overlay->image_width);
1234 pimage2 = pimage1 + 4 * overlay->image_width;
1235 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1236 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1237 for (j = 0; j < width; j += 2) {
1238 b = pimage1[CAIRO_ARGB_B];
1239 g = pimage1[CAIRO_ARGB_G];
1240 r = pimage1[CAIRO_ARGB_R];
1241 a = pimage1[CAIRO_ARGB_A];
1242 CAIRO_UNPREMULTIPLY (a, r, g, b);
1245 b1 = pimage1[CAIRO_ARGB_B];
1246 g1 = pimage1[CAIRO_ARGB_G];
1247 r1 = pimage1[CAIRO_ARGB_R];
1248 a1 = pimage1[CAIRO_ARGB_A];
1249 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1256 b1 = pimage2[CAIRO_ARGB_B];
1257 g1 = pimage2[CAIRO_ARGB_G];
1258 r1 = pimage2[CAIRO_ARGB_R];
1259 a1 = pimage2[CAIRO_ARGB_A];
1260 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1267 /* + 2 for rounding */
1268 b1 = pimage2[CAIRO_ARGB_B];
1269 g1 = pimage2[CAIRO_ARGB_G];
1270 r1 = pimage2[CAIRO_ARGB_R];
1271 a1 = pimage2[CAIRO_ARGB_A];
1272 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1289 COMP_U (cb, r, g, b);
1290 COMP_V (cr, r, g, b);
1293 BLEND (*pcb, a, cb, x);
1295 BLEND (*pcr, a, cr, x);
1304 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1305 const gchar * string, gint textlen)
1308 cairo_surface_t *surface;
1309 PangoRectangle ink_rect, logical_rect;
1310 cairo_matrix_t cairo_matrix;
1312 double scalef = 1.0;
1315 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1317 if (overlay->auto_adjust_size) {
1318 /* 640 pixel is default */
1319 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1321 pango_layout_set_width (overlay->layout, -1);
1322 /* set text on pango layout */
1323 pango_layout_set_markup (overlay->layout, string, textlen);
1325 /* get subtitle image size */
1326 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1328 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1330 if (width + overlay->deltax >
1331 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1333 * subtitle image width is larger then overlay width
1334 * so rearrange overlay wrap mode.
1336 gst_base_text_overlay_update_wrap_mode (overlay);
1337 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1338 width = overlay->width;
1342 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1343 if (height > overlay->height) {
1344 height = overlay->height;
1346 if (overlay->use_vertical_render) {
1347 PangoRectangle rect;
1348 PangoContext *context;
1349 PangoMatrix matrix = PANGO_MATRIX_INIT;
1352 context = pango_layout_get_context (overlay->layout);
1354 pango_matrix_rotate (&matrix, -90);
1356 rect.x = rect.y = 0;
1358 rect.height = height;
1359 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1360 matrix.x0 = -rect.x;
1361 matrix.y0 = -rect.y;
1363 pango_context_set_matrix (context, &matrix);
1365 cairo_matrix.xx = matrix.xx;
1366 cairo_matrix.yx = matrix.yx;
1367 cairo_matrix.xy = matrix.xy;
1368 cairo_matrix.yy = matrix.yy;
1369 cairo_matrix.x0 = matrix.x0;
1370 cairo_matrix.y0 = matrix.y0;
1371 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1377 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1380 /* reallocate surface */
1381 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1383 surface = cairo_image_surface_create_for_data (overlay->text_image,
1384 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1385 cr = cairo_create (surface);
1388 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1391 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1393 if (overlay->want_shading)
1394 cairo_paint_with_alpha (cr, overlay->shading_value);
1396 /* apply transformations */
1397 cairo_set_matrix (cr, &cairo_matrix);
1399 /* FIXME: We use show_layout everywhere except for the surface
1400 * because it's really faster and internally does all kinds of
1401 * caching. Unfortunately we have to paint to a cairo path for
1402 * the outline and this is slow. Once Pango supports user fonts
1403 * we should use them, see
1404 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1406 * Idea would the be, to create a cairo user font that
1407 * does shadow, outline, text painting in the
1408 * render_glyph function.
1411 /* draw shadow text */
1413 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1414 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1415 pango_cairo_show_layout (cr, overlay->layout);
1418 a = (overlay->outline_color >> 24) & 0xff;
1419 r = (overlay->outline_color >> 16) & 0xff;
1420 g = (overlay->outline_color >> 8) & 0xff;
1421 b = (overlay->outline_color >> 0) & 0xff;
1423 /* draw outline text */
1425 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1426 cairo_set_line_width (cr, overlay->outline_offset);
1427 pango_cairo_layout_path (cr, overlay->layout);
1431 a = (overlay->color >> 24) & 0xff;
1432 r = (overlay->color >> 16) & 0xff;
1433 g = (overlay->color >> 8) & 0xff;
1434 b = (overlay->color >> 0) & 0xff;
1438 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1439 pango_cairo_show_layout (cr, overlay->layout);
1443 cairo_surface_destroy (surface);
1444 overlay->image_width = width;
1445 overlay->image_height = height;
1446 overlay->baseline_y = ink_rect.y;
1447 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1454 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1455 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1457 gint i, j, dest_stride;
1460 dest_stride = dest->info.stride[0];
1461 dest_ptr = dest->data[0];
1463 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1464 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1466 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1467 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1469 for (i = y0; i < y1; ++i) {
1470 for (j = x0; j < x1; ++j) {
1471 gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
1473 dest_ptr[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1479 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1480 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1483 guint dest_stride, pixel_stride;
1486 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
1487 dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
1488 pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
1490 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1491 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1493 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1494 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1497 x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
1499 x1 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x1);
1502 y0 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y0);
1504 y1 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y1);
1506 for (i = y0; i < y1; i++) {
1507 for (j = x0; j < x1; j++) {
1511 y_pos = (i * dest_stride) + j * pixel_stride;
1512 y = dest_ptr[y_pos] + overlay->shading_value;
1514 dest_ptr[y_pos] = CLAMP (y, 0, 255);
1519 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1520 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1521 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1523 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
1524 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1529 dest_ptr = dest->data[0];
1531 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1532 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1534 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1535 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1537 for (i = y0; i < y1; i++) {
1538 for (j = x0; j < x1; j++) {
1541 y_pos = (i * 4 * overlay->width) + j * 4;
1542 for (k = 0; k < 4; k++) {
1543 y = dest_ptr[y_pos + k] + overlay->shading_value;
1544 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);
1550 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1551 static inline void \
1552 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
1553 gint x0, gint x1, gint y0, gint y1) \
1558 dest_ptr = dest->data[0];\
1560 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1561 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1563 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1564 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1566 for (i = y0; i < y1; i++) {\
1567 for (j = x0; j < x1; j++) {\
1569 y_pos = (i * 4 * overlay->width) + j * 4;\
1570 for (k = OFFSET; k < 3+OFFSET; k++) {\
1571 y = dest_ptr[y_pos + k] + overlay->shading_value;\
1572 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);\
1577 ARGB_SHADE_FUNCTION (ARGB, 1);
1578 ARGB_SHADE_FUNCTION (ABGR, 1);
1579 ARGB_SHADE_FUNCTION (RGBA, 0);
1580 ARGB_SHADE_FUNCTION (BGRA, 0);
1584 * - use proper strides and offset for I420
1585 * - don't draw over the edge of the picture (try a longer
1586 * text with a huge font size)
1590 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1591 GstVideoFrame * dest, gint xpos, gint ypos)
1593 int y_stride, u_stride, v_stride;
1594 guint8 *y_pixels, *u_pixels, *v_pixels;
1596 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1597 * to a boundary of integer number of U/V pixels:
1599 xpos = GST_ROUND_UP_2 (xpos);
1600 ypos = GST_ROUND_UP_2 (ypos);
1602 y_pixels = dest->data[0];
1603 u_pixels = dest->data[1];
1604 v_pixels = dest->data[2];
1605 y_stride = dest->info.stride[0];
1606 u_stride = dest->info.stride[1];
1607 v_stride = dest->info.stride[2];
1609 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1610 overlay->text_image, y_stride);
1611 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1612 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 2);
1616 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1617 GstVideoFrame * dest, gint xpos, gint ypos)
1619 int y_stride, u_stride, v_stride;
1620 guint8 *y_pixels, *u_pixels, *v_pixels;
1622 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1623 * to a boundary of integer number of U/V pixels:
1625 xpos = GST_ROUND_UP_2 (xpos);
1626 ypos = GST_ROUND_UP_2 (ypos);
1628 y_pixels = dest->data[0];
1629 u_pixels = dest->data[1];
1630 v_pixels = dest->data[2];
1631 y_stride = dest->info.stride[0];
1632 u_stride = dest->info.stride[1];
1633 v_stride = dest->info.stride[2];
1635 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1636 overlay->text_image, y_stride);
1637 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1638 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 1);
1642 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1643 GstVideoFrame * dest, gint xpos, gint ypos)
1650 guchar *pimage, *dest_ptr;
1653 yuv_pixels = dest->data[0];
1655 /* because U/V is 2x horizontally subsampled, we need to round to a
1656 * boundary of integer number of U/V pixels in x dimension:
1658 xpos = GST_ROUND_UP_2 (xpos);
1660 w = overlay->image_width - 2;
1661 h = overlay->image_height - 2;
1667 if (xpos + w > overlay->width) {
1668 w = overlay->width - xpos;
1671 if (ypos + h > overlay->height) {
1672 h = overlay->height - ypos;
1675 for (i = 0; i < h; i++) {
1676 pimage = overlay->text_image + i * overlay->image_width * 4;
1677 dest_ptr = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1678 for (j = 0; j < w; j += 2) {
1679 b0 = pimage[CAIRO_ARGB_B];
1680 g0 = pimage[CAIRO_ARGB_G];
1681 r0 = pimage[CAIRO_ARGB_R];
1682 a0 = pimage[CAIRO_ARGB_A];
1683 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1686 b1 = pimage[CAIRO_ARGB_B];
1687 g1 = pimage[CAIRO_ARGB_G];
1688 r1 = pimage[CAIRO_ARGB_R];
1689 a1 = pimage[CAIRO_ARGB_A];
1690 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1700 COMP_Y (y0, r0, g0, b0);
1701 COMP_Y (y1, r1, g1, b1);
1711 COMP_U (u, r0, g0, b0);
1712 COMP_V (v, r0, g0, b0);
1714 BLEND (*dest_ptr, a0, u, *dest_ptr);
1716 BLEND (*dest_ptr, a0, y0, *dest_ptr);
1718 BLEND (*dest_ptr, a0, v, *dest_ptr);
1720 BLEND (*dest_ptr, a0, y1, *dest_ptr);
1727 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1728 GstVideoFrame * dest, gint xpos, gint ypos)
1734 guchar *pimage, *dest_ptr;
1737 rgb_pixels = dest->data[0];
1739 w = overlay->image_width;
1740 h = overlay->image_height;
1746 if (xpos + w > overlay->width) {
1747 w = overlay->width - xpos;
1750 if (ypos + h > overlay->height) {
1751 h = overlay->height - ypos;
1754 for (i = 0; i < h; i++) {
1755 pimage = overlay->text_image + i * overlay->image_width * 4;
1756 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1757 for (j = 0; j < w; j++) {
1758 a = pimage[CAIRO_ARGB_A];
1759 b = pimage[CAIRO_ARGB_B];
1760 g = pimage[CAIRO_ARGB_G];
1761 r = pimage[CAIRO_ARGB_R];
1763 CAIRO_UNPREMULTIPLY (a, r, g, b);
1765 // convert background to yuv
1766 COMP_Y (y, r, g, b);
1767 COMP_U (u, r, g, b);
1768 COMP_V (v, r, g, b);
1770 // preform text "OVER" background alpha compositing
1771 a1 = a + (dest_ptr[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1772 OVER (dest_ptr[1], a, y, dest_ptr[0], dest_ptr[1], a1);
1773 OVER (dest_ptr[2], a, u, dest_ptr[0], dest_ptr[2], a1);
1774 OVER (dest_ptr[3], a, v, dest_ptr[0], dest_ptr[3], a1);
1775 dest_ptr[0] = a1 - 1; // remove the temporary 1 we added
1783 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1784 static inline void \
1785 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1786 GstVideoFrame * dest, gint xpos, gint ypos) \
1791 guchar *pimage, *dest_ptr; \
1792 guint8 *rgb_pixels;\
1794 rgb_pixels = dest->data[0];\
1796 w = overlay->image_width; \
1797 h = overlay->image_height; \
1803 if (xpos + w > overlay->width) { \
1804 w = overlay->width - xpos; \
1807 if (ypos + h > overlay->height) { \
1808 h = overlay->height - ypos; \
1811 for (i = 0; i < h; i++) { \
1812 pimage = overlay->text_image + i * overlay->image_width * 4; \
1813 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1814 for (j = 0; j < w; j++) { \
1815 a = pimage[CAIRO_ARGB_A]; \
1816 b = pimage[CAIRO_ARGB_B]; \
1817 g = pimage[CAIRO_ARGB_G]; \
1818 r = pimage[CAIRO_ARGB_R]; \
1819 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1820 b = (b*a + dest_ptr[B] * (255-a)) / 255; \
1821 g = (g*a + dest_ptr[G] * (255-a)) / 255; \
1822 r = (r*a + dest_ptr[R] * (255-a)) / 255; \
1832 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1833 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1834 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1835 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1837 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1838 static inline void \
1839 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1840 GstVideoFrame * dest, gint xpos, gint ypos) \
1842 int a, r, g, b, a1; \
1845 guchar *pimage, *dest_ptr; \
1846 guint8 *rgb_pixels;\
1848 rgb_pixels = dest->data[0];\
1850 w = overlay->image_width; \
1851 h = overlay->image_height; \
1857 if (xpos + w > overlay->width) { \
1858 w = overlay->width - xpos; \
1861 if (ypos + h > overlay->height) { \
1862 h = overlay->height - ypos; \
1865 for (i = 0; i < h; i++) { \
1866 pimage = overlay->text_image + i * overlay->image_width * 4; \
1867 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1868 for (j = 0; j < w; j++) { \
1869 a = pimage[CAIRO_ARGB_A]; \
1870 b = pimage[CAIRO_ARGB_B]; \
1871 g = pimage[CAIRO_ARGB_G]; \
1872 r = pimage[CAIRO_ARGB_R]; \
1873 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1874 a1 = a + (dest_ptr[A] * (255 - a)) / 255 + 1; \
1875 OVER (dest_ptr[R], a, r, dest_ptr[0], dest_ptr[R], a1); \
1876 OVER (dest_ptr[G], a, g, dest_ptr[0], dest_ptr[G], a1); \
1877 OVER (dest_ptr[B], a, b, dest_ptr[0], dest_ptr[B], a1); \
1878 dest_ptr[A] = a1 - 1; \
1884 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1885 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1886 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1887 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1890 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1891 const gchar * text, gint textlen)
1895 if (!overlay->need_render) {
1896 GST_DEBUG ("Using previously rendered text.");
1900 /* -1 is the whole string */
1901 if (text != NULL && textlen < 0) {
1902 textlen = strlen (text);
1906 string = g_strndup (text, textlen);
1907 } else { /* empty string */
1908 string = g_strdup (" ");
1910 g_strdelimit (string, "\r\t", ' ');
1911 textlen = strlen (string);
1913 /* FIXME: should we check for UTF-8 here? */
1915 GST_DEBUG ("Rendering '%s'", string);
1916 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1920 overlay->need_render = FALSE;
1923 static GstFlowReturn
1924 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1925 GstBuffer * video_frame)
1929 GstBaseTextOverlayVAlign valign;
1930 GstBaseTextOverlayHAlign halign;
1931 GstVideoFrame frame;
1933 width = overlay->image_width;
1934 height = overlay->image_height;
1936 video_frame = gst_buffer_make_writable (video_frame);
1938 if (!gst_video_frame_map (&frame, &overlay->info, video_frame, GST_MAP_WRITE))
1941 if (overlay->use_vertical_render)
1942 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1944 halign = overlay->halign;
1947 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1948 xpos = overlay->xpad;
1950 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1951 xpos = (overlay->width - width) / 2;
1953 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1954 xpos = overlay->width - width - overlay->xpad;
1956 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1957 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1958 xpos = CLAMP (xpos, 0, overlay->width - width);
1965 xpos += overlay->deltax;
1967 if (overlay->use_vertical_render)
1968 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1970 valign = overlay->valign;
1973 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1974 ypos = overlay->height - height - overlay->ypad;
1976 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1977 ypos = overlay->height - (height + overlay->ypad);
1979 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1980 ypos = overlay->ypad;
1982 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1983 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1984 ypos = CLAMP (ypos, 0, overlay->height - height);
1986 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1987 ypos = (overlay->height - height) / 2;
1990 ypos = overlay->ypad;
1993 ypos += overlay->deltay;
1995 /* shaded background box */
1996 if (overlay->want_shading) {
1997 switch (overlay->format) {
1998 case GST_VIDEO_FORMAT_I420:
1999 case GST_VIDEO_FORMAT_NV12:
2000 case GST_VIDEO_FORMAT_NV21:
2001 gst_base_text_overlay_shade_planar_Y (overlay, &frame,
2002 xpos, xpos + overlay->image_width,
2003 ypos, ypos + overlay->image_height);
2005 case GST_VIDEO_FORMAT_AYUV:
2006 case GST_VIDEO_FORMAT_UYVY:
2007 gst_base_text_overlay_shade_packed_Y (overlay, &frame,
2008 xpos, xpos + overlay->image_width,
2009 ypos, ypos + overlay->image_height);
2011 case GST_VIDEO_FORMAT_xRGB:
2012 gst_base_text_overlay_shade_xRGB (overlay, &frame,
2013 xpos, xpos + overlay->image_width,
2014 ypos, ypos + overlay->image_height);
2016 case GST_VIDEO_FORMAT_xBGR:
2017 gst_base_text_overlay_shade_xBGR (overlay, &frame,
2018 xpos, xpos + overlay->image_width,
2019 ypos, ypos + overlay->image_height);
2021 case GST_VIDEO_FORMAT_BGRx:
2022 gst_base_text_overlay_shade_BGRx (overlay, &frame,
2023 xpos, xpos + overlay->image_width,
2024 ypos, ypos + overlay->image_height);
2026 case GST_VIDEO_FORMAT_RGBx:
2027 gst_base_text_overlay_shade_RGBx (overlay, &frame,
2028 xpos, xpos + overlay->image_width,
2029 ypos, ypos + overlay->image_height);
2031 case GST_VIDEO_FORMAT_ARGB:
2032 gst_base_text_overlay_shade_ARGB (overlay, &frame,
2033 xpos, xpos + overlay->image_width,
2034 ypos, ypos + overlay->image_height);
2036 case GST_VIDEO_FORMAT_ABGR:
2037 gst_base_text_overlay_shade_ABGR (overlay, &frame,
2038 xpos, xpos + overlay->image_width,
2039 ypos, ypos + overlay->image_height);
2041 case GST_VIDEO_FORMAT_RGBA:
2042 gst_base_text_overlay_shade_RGBA (overlay, &frame,
2043 xpos, xpos + overlay->image_width,
2044 ypos, ypos + overlay->image_height);
2046 case GST_VIDEO_FORMAT_BGRA:
2047 gst_base_text_overlay_shade_BGRA (overlay, &frame,
2048 xpos, xpos + overlay->image_width,
2049 ypos, ypos + overlay->image_height);
2052 g_assert_not_reached ();
2059 if (overlay->text_image) {
2060 switch (overlay->format) {
2061 case GST_VIDEO_FORMAT_I420:
2062 gst_base_text_overlay_blit_I420 (overlay, &frame, xpos, ypos);
2064 case GST_VIDEO_FORMAT_NV12:
2065 case GST_VIDEO_FORMAT_NV21:
2066 gst_base_text_overlay_blit_NV12_NV21 (overlay, &frame, xpos, ypos);
2068 case GST_VIDEO_FORMAT_UYVY:
2069 gst_base_text_overlay_blit_UYVY (overlay, &frame, xpos, ypos);
2071 case GST_VIDEO_FORMAT_AYUV:
2072 gst_base_text_overlay_blit_AYUV (overlay, &frame, xpos, ypos);
2074 case GST_VIDEO_FORMAT_BGRx:
2075 gst_base_text_overlay_blit_BGRx (overlay, &frame, xpos, ypos);
2077 case GST_VIDEO_FORMAT_xRGB:
2078 gst_base_text_overlay_blit_xRGB (overlay, &frame, xpos, ypos);
2080 case GST_VIDEO_FORMAT_RGBx:
2081 gst_base_text_overlay_blit_RGBx (overlay, &frame, xpos, ypos);
2083 case GST_VIDEO_FORMAT_xBGR:
2084 gst_base_text_overlay_blit_xBGR (overlay, &frame, xpos, ypos);
2086 case GST_VIDEO_FORMAT_ARGB:
2087 gst_base_text_overlay_blit_ARGB (overlay, &frame, xpos, ypos);
2089 case GST_VIDEO_FORMAT_ABGR:
2090 gst_base_text_overlay_blit_ABGR (overlay, &frame, xpos, ypos);
2092 case GST_VIDEO_FORMAT_RGBA:
2093 gst_base_text_overlay_blit_RGBA (overlay, &frame, xpos, ypos);
2095 case GST_VIDEO_FORMAT_BGRA:
2096 gst_base_text_overlay_blit_BGRA (overlay, &frame, xpos, ypos);
2099 g_assert_not_reached ();
2102 gst_video_frame_unmap (&frame);
2104 return gst_pad_push (overlay->srcpad, video_frame);
2109 GST_DEBUG_OBJECT (overlay, "received invalid buffer");
2114 static GstPadLinkReturn
2115 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2117 GstBaseTextOverlay *overlay;
2119 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2120 if (G_UNLIKELY (!overlay))
2121 return GST_PAD_LINK_REFUSED;
2123 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2125 overlay->text_linked = TRUE;
2127 gst_object_unref (overlay);
2129 return GST_PAD_LINK_OK;
2133 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2135 GstBaseTextOverlay *overlay;
2137 /* don't use gst_pad_get_parent() here, will deadlock */
2138 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2140 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2142 overlay->text_linked = FALSE;
2144 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2148 gst_base_text_overlay_text_event (GstPad * pad, GstObject * parent,
2151 gboolean ret = FALSE;
2152 GstBaseTextOverlay *overlay = NULL;
2154 overlay = GST_BASE_TEXT_OVERLAY (parent);
2156 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2158 switch (GST_EVENT_TYPE (event)) {
2159 case GST_EVENT_CAPS:
2163 gst_event_parse_caps (event, &caps);
2164 ret = gst_base_text_overlay_setcaps_txt (overlay, caps);
2165 gst_event_unref (event);
2168 case GST_EVENT_SEGMENT:
2170 const GstSegment *segment;
2172 overlay->text_eos = FALSE;
2174 gst_event_parse_segment (event, &segment);
2176 if (segment->format == GST_FORMAT_TIME) {
2177 GST_OBJECT_LOCK (overlay);
2178 gst_segment_copy_into (segment, &overlay->text_segment);
2179 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2180 &overlay->text_segment);
2181 GST_OBJECT_UNLOCK (overlay);
2183 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2184 ("received non-TIME newsegment event on text input"));
2187 gst_event_unref (event);
2190 /* wake up the video chain, it might be waiting for a text buffer or
2191 * a text segment update */
2192 GST_OBJECT_LOCK (overlay);
2193 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2194 GST_OBJECT_UNLOCK (overlay);
2197 case GST_EVENT_FLUSH_STOP:
2198 GST_OBJECT_LOCK (overlay);
2199 GST_INFO_OBJECT (overlay, "text flush stop");
2200 overlay->text_flushing = FALSE;
2201 overlay->text_eos = FALSE;
2202 gst_base_text_overlay_pop_text (overlay);
2203 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2204 GST_OBJECT_UNLOCK (overlay);
2205 gst_event_unref (event);
2208 case GST_EVENT_FLUSH_START:
2209 GST_OBJECT_LOCK (overlay);
2210 GST_INFO_OBJECT (overlay, "text flush start");
2211 overlay->text_flushing = TRUE;
2212 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2213 GST_OBJECT_UNLOCK (overlay);
2214 gst_event_unref (event);
2218 GST_OBJECT_LOCK (overlay);
2219 overlay->text_eos = TRUE;
2220 GST_INFO_OBJECT (overlay, "text EOS");
2221 /* wake up the video chain, it might be waiting for a text buffer or
2222 * a text segment update */
2223 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2224 GST_OBJECT_UNLOCK (overlay);
2225 gst_event_unref (event);
2229 ret = gst_pad_event_default (pad, parent, event);
2237 gst_base_text_overlay_video_event (GstPad * pad, GstObject * parent,
2240 gboolean ret = FALSE;
2241 GstBaseTextOverlay *overlay = NULL;
2243 overlay = GST_BASE_TEXT_OVERLAY (parent);
2245 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2247 switch (GST_EVENT_TYPE (event)) {
2248 case GST_EVENT_CAPS:
2252 gst_event_parse_caps (event, &caps);
2253 ret = gst_base_text_overlay_setcaps (overlay, caps);
2254 gst_event_unref (event);
2257 case GST_EVENT_SEGMENT:
2259 const GstSegment *segment;
2261 GST_DEBUG_OBJECT (overlay, "received new segment");
2263 gst_event_parse_segment (event, &segment);
2265 if (segment->format == GST_FORMAT_TIME) {
2266 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2269 gst_segment_copy_into (segment, &overlay->segment);
2271 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2272 ("received non-TIME newsegment event on video input"));
2275 ret = gst_pad_event_default (pad, parent, event);
2279 GST_OBJECT_LOCK (overlay);
2280 GST_INFO_OBJECT (overlay, "video EOS");
2281 overlay->video_eos = TRUE;
2282 GST_OBJECT_UNLOCK (overlay);
2283 ret = gst_pad_event_default (pad, parent, event);
2285 case GST_EVENT_FLUSH_START:
2286 GST_OBJECT_LOCK (overlay);
2287 GST_INFO_OBJECT (overlay, "video flush start");
2288 overlay->video_flushing = TRUE;
2289 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2290 GST_OBJECT_UNLOCK (overlay);
2291 ret = gst_pad_event_default (pad, parent, event);
2293 case GST_EVENT_FLUSH_STOP:
2294 GST_OBJECT_LOCK (overlay);
2295 GST_INFO_OBJECT (overlay, "video flush stop");
2296 overlay->video_flushing = FALSE;
2297 overlay->video_eos = FALSE;
2298 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2299 GST_OBJECT_UNLOCK (overlay);
2300 ret = gst_pad_event_default (pad, parent, event);
2303 ret = gst_pad_event_default (pad, parent, event);
2311 gst_base_text_overlay_video_query (GstPad * pad, GstObject * parent,
2314 gboolean ret = FALSE;
2316 switch (GST_QUERY_TYPE (query)) {
2317 case GST_QUERY_CAPS:
2319 GstCaps *filter, *caps;
2321 gst_query_parse_caps (query, &filter);
2322 caps = gst_base_text_overlay_getcaps (pad, filter);
2323 gst_query_set_caps_result (query, caps);
2324 gst_caps_unref (caps);
2329 ret = gst_pad_query_default (pad, parent, query);
2336 /* Called with lock held */
2338 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2340 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2342 if (overlay->text_buffer) {
2343 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2344 overlay->text_buffer);
2345 gst_buffer_unref (overlay->text_buffer);
2346 overlay->text_buffer = NULL;
2349 /* Let the text task know we used that buffer */
2350 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2353 /* We receive text buffers here. If they are out of segment we just ignore them.
2354 If the buffer is in our segment we keep it internally except if another one
2355 is already waiting here, in that case we wait that it gets kicked out */
2356 static GstFlowReturn
2357 gst_base_text_overlay_text_chain (GstPad * pad, GstObject * parent,
2360 GstFlowReturn ret = GST_FLOW_OK;
2361 GstBaseTextOverlay *overlay = NULL;
2362 gboolean in_seg = FALSE;
2363 guint64 clip_start = 0, clip_stop = 0;
2365 overlay = GST_BASE_TEXT_OVERLAY (parent);
2367 GST_OBJECT_LOCK (overlay);
2369 if (overlay->text_flushing) {
2370 GST_OBJECT_UNLOCK (overlay);
2371 ret = GST_FLOW_FLUSHING;
2372 GST_LOG_OBJECT (overlay, "text flushing");
2376 if (overlay->text_eos) {
2377 GST_OBJECT_UNLOCK (overlay);
2379 GST_LOG_OBJECT (overlay, "text EOS");
2383 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2384 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2385 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2386 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2387 GST_BUFFER_DURATION (buffer)));
2389 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2392 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2393 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2395 stop = GST_CLOCK_TIME_NONE;
2397 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2398 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2404 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2405 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2406 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2407 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2409 /* Wait for the previous buffer to go away */
2410 while (overlay->text_buffer != NULL) {
2411 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2412 GST_DEBUG_PAD_NAME (pad));
2413 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2414 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2415 if (overlay->text_flushing) {
2416 GST_OBJECT_UNLOCK (overlay);
2417 ret = GST_FLOW_FLUSHING;
2422 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2423 overlay->text_segment.position = clip_start;
2425 overlay->text_buffer = buffer;
2426 /* That's a new text buffer we need to render */
2427 overlay->need_render = TRUE;
2429 /* in case the video chain is waiting for a text buffer, wake it up */
2430 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2433 GST_OBJECT_UNLOCK (overlay);
2440 static GstFlowReturn
2441 gst_base_text_overlay_video_chain (GstPad * pad, GstObject * parent,
2444 GstBaseTextOverlayClass *klass;
2445 GstBaseTextOverlay *overlay;
2446 GstFlowReturn ret = GST_FLOW_OK;
2447 gboolean in_seg = FALSE;
2448 guint64 start, stop, clip_start = 0, clip_stop = 0;
2451 overlay = GST_BASE_TEXT_OVERLAY (parent);
2452 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2454 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2455 goto missing_timestamp;
2457 /* ignore buffers that are outside of the current segment */
2458 start = GST_BUFFER_TIMESTAMP (buffer);
2460 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2461 stop = GST_CLOCK_TIME_NONE;
2463 stop = start + GST_BUFFER_DURATION (buffer);
2466 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2467 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2468 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2470 /* segment_clip() will adjust start unconditionally to segment_start if
2471 * no stop time is provided, so handle this ourselves */
2472 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2473 goto out_of_segment;
2475 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2476 &clip_start, &clip_stop);
2479 goto out_of_segment;
2481 /* if the buffer is only partially in the segment, fix up stamps */
2482 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2483 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2484 buffer = gst_buffer_make_writable (buffer);
2485 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2487 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2490 /* now, after we've done the clipping, fix up end time if there's no
2491 * duration (we only use those estimated values internally though, we
2492 * don't want to set bogus values on the buffer itself) */
2496 gint fps_num, fps_denom;
2498 /* FIXME, store this in setcaps */
2499 caps = gst_pad_get_current_caps (pad);
2500 s = gst_caps_get_structure (caps, 0);
2501 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2502 fps_num && fps_denom) {
2503 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2504 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2506 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2507 stop = start + 1; /* we need to assume some interval */
2509 gst_caps_unref (caps);
2512 gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2516 GST_OBJECT_LOCK (overlay);
2518 if (overlay->video_flushing)
2521 if (overlay->video_eos)
2524 if (overlay->silent) {
2525 GST_OBJECT_UNLOCK (overlay);
2526 ret = gst_pad_push (overlay->srcpad, buffer);
2528 /* Update position */
2529 overlay->segment.position = clip_start;
2534 /* Text pad not linked, rendering internal text */
2535 if (!overlay->text_linked) {
2536 if (klass->get_text) {
2537 text = klass->get_text (overlay, buffer);
2539 text = g_strdup (overlay->default_text);
2542 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2543 "text: '%s'", GST_STR_NULL (text));
2545 GST_OBJECT_UNLOCK (overlay);
2547 if (text != NULL && *text != '\0') {
2548 /* Render and push */
2549 gst_base_text_overlay_render_text (overlay, text, -1);
2550 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2552 /* Invalid or empty string */
2553 ret = gst_pad_push (overlay->srcpad, buffer);
2556 /* Text pad linked, check if we have a text buffer queued */
2557 if (overlay->text_buffer) {
2558 gboolean pop_text = FALSE, valid_text_time = TRUE;
2559 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2560 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2561 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2562 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2563 GstClockTime vid_running_time, vid_running_time_end;
2565 /* if the text buffer isn't stamped right, pop it off the
2566 * queue and display it for the current video frame only */
2567 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2568 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2569 GST_WARNING_OBJECT (overlay,
2570 "Got text buffer with invalid timestamp or duration");
2572 valid_text_time = FALSE;
2574 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2575 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2579 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2581 vid_running_time_end =
2582 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2585 /* If timestamp and duration are valid */
2586 if (valid_text_time) {
2588 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2590 text_running_time_end =
2591 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2595 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2596 GST_TIME_ARGS (text_running_time),
2597 GST_TIME_ARGS (text_running_time_end));
2598 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2599 GST_TIME_ARGS (vid_running_time),
2600 GST_TIME_ARGS (vid_running_time_end));
2602 /* Text too old or in the future */
2603 if (valid_text_time && text_running_time_end <= vid_running_time) {
2604 /* text buffer too old, get rid of it and do nothing */
2605 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2607 gst_base_text_overlay_pop_text (overlay);
2608 GST_OBJECT_UNLOCK (overlay);
2609 goto wait_for_text_buf;
2610 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2611 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2612 GST_OBJECT_UNLOCK (overlay);
2613 /* Push the video frame */
2614 ret = gst_pad_push (overlay->srcpad, buffer);
2620 gst_buffer_map (overlay->text_buffer, &map, GST_MAP_READ);
2621 in_text = (gchar *) map.data;
2624 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2625 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2626 * here on purpose, this is something that needs fixing upstream */
2627 if (!g_utf8_validate (in_text, in_size, NULL)) {
2628 const gchar *end = NULL;
2630 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2631 in_text = g_strndup (in_text, in_size);
2632 while (!g_utf8_validate (in_text, in_size, &end) && end)
2633 *((gchar *) end) = '*';
2636 /* Get the string */
2637 if (overlay->have_pango_markup) {
2638 text = g_strndup (in_text, in_size);
2640 text = g_markup_escape_text (in_text, in_size);
2643 if (text != NULL && *text != '\0') {
2644 gint text_len = strlen (text);
2646 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2647 text[text_len - 1] == '\r')) {
2650 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2651 gst_base_text_overlay_render_text (overlay, text, text_len);
2653 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2654 gst_base_text_overlay_render_text (overlay, " ", 1);
2656 if (in_text != (gchar *) map.data)
2659 gst_buffer_unmap (overlay->text_buffer, &map);
2661 GST_OBJECT_UNLOCK (overlay);
2662 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2664 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2665 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2670 GST_OBJECT_LOCK (overlay);
2671 gst_base_text_overlay_pop_text (overlay);
2672 GST_OBJECT_UNLOCK (overlay);
2675 gboolean wait_for_text_buf = TRUE;
2677 if (overlay->text_eos)
2678 wait_for_text_buf = FALSE;
2680 if (!overlay->wait_text)
2681 wait_for_text_buf = FALSE;
2683 /* Text pad linked, but no text buffer available - what now? */
2684 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2685 GstClockTime text_start_running_time, text_position_running_time;
2686 GstClockTime vid_running_time;
2689 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2690 GST_BUFFER_TIMESTAMP (buffer));
2691 text_start_running_time =
2692 gst_segment_to_running_time (&overlay->text_segment,
2693 GST_FORMAT_TIME, overlay->text_segment.start);
2694 text_position_running_time =
2695 gst_segment_to_running_time (&overlay->text_segment,
2696 GST_FORMAT_TIME, overlay->text_segment.position);
2698 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2699 vid_running_time < text_start_running_time) ||
2700 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2701 vid_running_time < text_position_running_time)) {
2702 wait_for_text_buf = FALSE;
2706 if (wait_for_text_buf) {
2707 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2708 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2709 GST_DEBUG_OBJECT (overlay, "resuming");
2710 GST_OBJECT_UNLOCK (overlay);
2711 goto wait_for_text_buf;
2713 GST_OBJECT_UNLOCK (overlay);
2714 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2715 ret = gst_pad_push (overlay->srcpad, buffer);
2722 /* Update position */
2723 overlay->segment.position = clip_start;
2729 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2730 gst_buffer_unref (buffer);
2736 GST_OBJECT_UNLOCK (overlay);
2737 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2738 gst_buffer_unref (buffer);
2739 return GST_FLOW_FLUSHING;
2743 GST_OBJECT_UNLOCK (overlay);
2744 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2745 gst_buffer_unref (buffer);
2746 return GST_FLOW_EOS;
2750 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2751 gst_buffer_unref (buffer);
2756 static GstStateChangeReturn
2757 gst_base_text_overlay_change_state (GstElement * element,
2758 GstStateChange transition)
2760 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2761 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2763 switch (transition) {
2764 case GST_STATE_CHANGE_PAUSED_TO_READY:
2765 GST_OBJECT_LOCK (overlay);
2766 overlay->text_flushing = TRUE;
2767 overlay->video_flushing = TRUE;
2768 /* pop_text will broadcast on the GCond and thus also make the video
2769 * chain exit if it's waiting for a text buffer */
2770 gst_base_text_overlay_pop_text (overlay);
2771 GST_OBJECT_UNLOCK (overlay);
2777 ret = parent_class->change_state (element, transition);
2778 if (ret == GST_STATE_CHANGE_FAILURE)
2781 switch (transition) {
2782 case GST_STATE_CHANGE_READY_TO_PAUSED:
2783 GST_OBJECT_LOCK (overlay);
2784 overlay->text_flushing = FALSE;
2785 overlay->video_flushing = FALSE;
2786 overlay->video_eos = FALSE;
2787 overlay->text_eos = FALSE;
2788 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2789 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2790 GST_OBJECT_UNLOCK (overlay);
2800 plugin_init (GstPlugin * plugin)
2802 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2803 GST_TYPE_TEXT_OVERLAY) ||
2804 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2805 GST_TYPE_TIME_OVERLAY) ||
2806 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2807 GST_TYPE_CLOCK_OVERLAY) ||
2808 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2809 GST_TYPE_TEXT_RENDER)) {
2813 /*texttestsrc_plugin_init(module, plugin); */
2815 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2820 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2821 "pango", "Pango-based text rendering and overlay", plugin_init,
2822 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)