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,
311 GstBaseTextOverlay * overlay, GstCaps * filter);
312 static gboolean gst_base_text_overlay_setcaps (GstBaseTextOverlay * overlay,
314 static gboolean gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay,
316 static gboolean gst_base_text_overlay_src_event (GstPad * pad,
317 GstObject * parent, GstEvent * event);
318 static gboolean gst_base_text_overlay_src_query (GstPad * pad,
319 GstObject * parent, GstQuery * query);
321 static gboolean gst_base_text_overlay_video_event (GstPad * pad,
322 GstObject * parent, GstEvent * event);
323 static gboolean gst_base_text_overlay_video_query (GstPad * pad,
324 GstObject * parent, GstQuery * query);
325 static GstFlowReturn gst_base_text_overlay_video_chain (GstPad * pad,
326 GstObject * parent, GstBuffer * buffer);
328 static gboolean gst_base_text_overlay_text_event (GstPad * pad,
329 GstObject * parent, GstEvent * event);
330 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
331 GstObject * parent, GstBuffer * buffer);
332 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
334 static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
335 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
336 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
339 static void gst_base_text_overlay_finalize (GObject * object);
340 static void gst_base_text_overlay_set_property (GObject * object, guint prop_id,
341 const GValue * value, GParamSpec * pspec);
342 static void gst_base_text_overlay_get_property (GObject * object, guint prop_id,
343 GValue * value, GParamSpec * pspec);
345 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
346 PangoFontDescription * desc);
349 gst_base_text_overlay_get_type (void)
351 static GType type = 0;
353 if (g_once_init_enter ((gsize *) & type)) {
354 static const GTypeInfo info = {
355 sizeof (GstBaseTextOverlayClass),
356 (GBaseInitFunc) gst_base_text_overlay_base_init,
358 (GClassInitFunc) gst_base_text_overlay_class_init,
361 sizeof (GstBaseTextOverlay),
363 (GInstanceInitFunc) gst_base_text_overlay_init,
366 g_once_init_leave ((gsize *) & type,
367 g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTextOverlay", &info,
375 gst_base_text_overlay_get_text (GstBaseTextOverlay * overlay,
376 GstBuffer * video_frame)
378 return g_strdup (overlay->default_text);
382 gst_base_text_overlay_base_init (gpointer g_class)
384 GstBaseTextOverlayClass *klass = GST_BASE_TEXT_OVERLAY_CLASS (g_class);
385 PangoFontMap *fontmap;
387 /* Only lock for the subclasses here, the base class
388 * doesn't have this mutex yet and it's not necessary
390 if (klass->pango_lock)
391 g_mutex_lock (klass->pango_lock);
392 fontmap = pango_cairo_font_map_get_default ();
393 klass->pango_context =
394 pango_font_map_create_context (PANGO_FONT_MAP (fontmap));
395 if (klass->pango_lock)
396 g_mutex_unlock (klass->pango_lock);
400 gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
402 GObjectClass *gobject_class;
403 GstElementClass *gstelement_class;
405 gobject_class = (GObjectClass *) klass;
406 gstelement_class = (GstElementClass *) klass;
408 parent_class = g_type_class_peek_parent (klass);
410 gobject_class->finalize = gst_base_text_overlay_finalize;
411 gobject_class->set_property = gst_base_text_overlay_set_property;
412 gobject_class->get_property = gst_base_text_overlay_get_property;
414 gst_element_class_add_pad_template (gstelement_class,
415 gst_static_pad_template_get (&src_template_factory));
416 gst_element_class_add_pad_template (gstelement_class,
417 gst_static_pad_template_get (&video_sink_template_factory));
419 gstelement_class->change_state =
420 GST_DEBUG_FUNCPTR (gst_base_text_overlay_change_state);
422 klass->pango_lock = g_slice_new (GMutex);
423 g_mutex_init (klass->pango_lock);
425 klass->get_text = gst_base_text_overlay_get_text;
427 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
428 g_param_spec_string ("text", "text",
429 "Text to be display.", DEFAULT_PROP_TEXT,
430 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
431 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
432 g_param_spec_boolean ("shaded-background", "shaded background",
433 "Whether to shade the background under the text area",
434 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
435 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
436 g_param_spec_enum ("valignment", "vertical alignment",
437 "Vertical alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_VALIGN,
438 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
439 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
440 g_param_spec_enum ("halignment", "horizontal alignment",
441 "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
442 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
443 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
444 g_param_spec_string ("valign", "vertical alignment",
445 "Vertical alignment of the text (deprecated; use valignment)",
446 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
447 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
448 g_param_spec_string ("halign", "horizontal alignment",
449 "Horizontal alignment of the text (deprecated; use halignment)",
450 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
451 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
452 g_param_spec_int ("xpad", "horizontal paddding",
453 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
454 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
455 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
456 g_param_spec_int ("ypad", "vertical padding",
457 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
458 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
459 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
460 g_param_spec_int ("deltax", "X position modifier",
461 "Shift X position to the left or to the right. Unit is pixels.",
462 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
463 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
464 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
465 g_param_spec_int ("deltay", "Y position modifier",
466 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
467 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
469 * GstBaseTextOverlay:xpos
471 * Horizontal position of the rendered text when using positioned alignment.
475 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
476 g_param_spec_double ("xpos", "horizontal position",
477 "Horizontal position when using position alignment", 0, 1.0,
479 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
481 * GstBaseTextOverlay:ypos
483 * Vertical position of the rendered text when using positioned alignment.
487 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
488 g_param_spec_double ("ypos", "vertical position",
489 "Vertical position when using position alignment", 0, 1.0,
491 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
492 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
493 g_param_spec_enum ("wrap-mode", "wrap mode",
494 "Whether to wrap the text and if so how.",
495 GST_TYPE_BASE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
496 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
497 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
498 g_param_spec_string ("font-desc", "font description",
499 "Pango font description of font to be used for rendering. "
500 "See documentation of pango_font_description_from_string "
501 "for syntax.", DEFAULT_PROP_FONT_DESC,
502 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
504 * GstBaseTextOverlay:color
506 * Color of the rendered text.
510 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
511 g_param_spec_uint ("color", "Color",
512 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
514 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
516 * GstTextOverlay:outline-color
518 * Color of the outline of the rendered text.
522 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
523 g_param_spec_uint ("outline-color", "Text Outline Color",
524 "Color to use for outline the text (big-endian ARGB).", 0,
525 G_MAXUINT32, DEFAULT_PROP_OUTLINE_COLOR,
526 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
529 * GstBaseTextOverlay:line-alignment
531 * Alignment of text lines relative to each other (for multi-line text)
535 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
536 g_param_spec_enum ("line-alignment", "line alignment",
537 "Alignment of text lines relative to each other.",
538 GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
539 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
541 * GstBaseTextOverlay:silent
543 * If set, no text is rendered. Useful to switch off text rendering
544 * temporarily without removing the textoverlay element from the pipeline.
548 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
549 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
550 g_param_spec_boolean ("silent", "silent",
551 "Whether to render the text string",
553 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
555 * GstBaseTextOverlay:wait-text
557 * If set, the video will block until a subtitle is received on the text pad.
558 * If video and subtitles are sent in sync, like from the same demuxer, this
559 * property should be set.
563 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
564 g_param_spec_boolean ("wait-text", "Wait Text",
565 "Whether to wait for subtitles",
566 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
568 g_object_class_install_property (G_OBJECT_CLASS (klass),
569 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
570 "Automatically adjust font size to screen-size.",
571 DEFAULT_PROP_AUTO_ADJUST_SIZE,
572 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
574 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
575 g_param_spec_boolean ("vertical-render", "vertical render",
576 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
577 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
581 gst_base_text_overlay_finalize (GObject * object)
583 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (object);
585 g_free (overlay->default_text);
587 if (overlay->text_image) {
588 g_free (overlay->text_image);
589 overlay->text_image = NULL;
592 if (overlay->layout) {
593 g_object_unref (overlay->layout);
594 overlay->layout = NULL;
597 if (overlay->text_buffer) {
598 gst_buffer_unref (overlay->text_buffer);
599 overlay->text_buffer = NULL;
602 g_cond_clear (&overlay->cond);
604 G_OBJECT_CLASS (parent_class)->finalize (object);
608 gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
609 GstBaseTextOverlayClass * klass)
611 GstPadTemplate *template;
612 PangoFontDescription *desc;
615 template = gst_static_pad_template_get (&video_sink_template_factory);
616 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
617 gst_object_unref (template);
618 gst_pad_set_event_function (overlay->video_sinkpad,
619 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_event));
620 gst_pad_set_chain_function (overlay->video_sinkpad,
621 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
622 gst_pad_set_query_function (overlay->video_sinkpad,
623 GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_query));
624 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
627 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass),
631 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
632 gst_object_unref (template);
634 gst_pad_set_event_function (overlay->text_sinkpad,
635 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
636 gst_pad_set_chain_function (overlay->text_sinkpad,
637 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_chain));
638 gst_pad_set_link_function (overlay->text_sinkpad,
639 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_link));
640 gst_pad_set_unlink_function (overlay->text_sinkpad,
641 GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_pad_unlink));
642 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
646 template = gst_static_pad_template_get (&src_template_factory);
647 overlay->srcpad = gst_pad_new_from_template (template, "src");
648 gst_object_unref (template);
649 gst_pad_set_event_function (overlay->srcpad,
650 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_event));
651 gst_pad_set_query_function (overlay->srcpad,
652 GST_DEBUG_FUNCPTR (gst_base_text_overlay_src_query));
653 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
655 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
656 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
658 pango_layout_new (GST_BASE_TEXT_OVERLAY_GET_CLASS
659 (overlay)->pango_context);
661 pango_context_get_font_description (GST_BASE_TEXT_OVERLAY_GET_CLASS
662 (overlay)->pango_context);
663 gst_base_text_overlay_adjust_values_with_fontdesc (overlay, desc);
665 overlay->color = DEFAULT_PROP_COLOR;
666 overlay->outline_color = DEFAULT_PROP_OUTLINE_COLOR;
667 overlay->halign = DEFAULT_PROP_HALIGNMENT;
668 overlay->valign = DEFAULT_PROP_VALIGNMENT;
669 overlay->xpad = DEFAULT_PROP_XPAD;
670 overlay->ypad = DEFAULT_PROP_YPAD;
671 overlay->deltax = DEFAULT_PROP_DELTAX;
672 overlay->deltay = DEFAULT_PROP_DELTAY;
673 overlay->xpos = DEFAULT_PROP_XPOS;
674 overlay->ypos = DEFAULT_PROP_YPOS;
676 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
678 overlay->want_shading = DEFAULT_PROP_SHADING;
679 overlay->shading_value = DEFAULT_SHADING_VALUE;
680 overlay->silent = DEFAULT_PROP_SILENT;
681 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
682 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
684 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
685 overlay->need_render = TRUE;
686 overlay->text_image = NULL;
687 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
688 gst_base_text_overlay_update_render_mode (overlay);
690 overlay->text_buffer = NULL;
691 overlay->text_linked = FALSE;
692 g_cond_init (&overlay->cond);
693 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
694 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
698 gst_base_text_overlay_update_wrap_mode (GstBaseTextOverlay * overlay)
700 if (overlay->wrap_mode == GST_BASE_TEXT_OVERLAY_WRAP_MODE_NONE) {
701 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
702 pango_layout_set_width (overlay->layout, -1);
706 if (overlay->auto_adjust_size) {
707 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
708 if (overlay->use_vertical_render) {
709 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
713 (overlay->use_vertical_render ? overlay->height : overlay->width) *
717 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
718 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
719 pango_layout_set_width (overlay->layout, width);
720 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
725 gst_base_text_overlay_update_render_mode (GstBaseTextOverlay * overlay)
727 PangoMatrix matrix = PANGO_MATRIX_INIT;
728 PangoContext *context = pango_layout_get_context (overlay->layout);
730 if (overlay->use_vertical_render) {
731 pango_matrix_rotate (&matrix, -90);
732 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
733 pango_context_set_matrix (context, &matrix);
734 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
736 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
737 pango_context_set_matrix (context, &matrix);
738 pango_layout_set_alignment (overlay->layout,
739 (PangoAlignment) 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;
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, overlay, 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, GstBaseTextOverlay * overlay,
1092 if (G_UNLIKELY (!overlay))
1093 return 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);
1135 gst_base_text_overlay_adjust_values_with_fontdesc (GstBaseTextOverlay * overlay,
1136 PangoFontDescription * desc)
1138 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1139 overlay->shadow_offset = (double) (font_size) / 13.0;
1140 overlay->outline_offset = (double) (font_size) / 15.0;
1141 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1142 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1145 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1146 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1147 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1148 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1152 gst_base_text_overlay_blit_1 (GstBaseTextOverlay * overlay, guchar * dest,
1153 gint xpos, gint ypos, guchar * text_image, guint dest_stride)
1160 gint width = overlay->image_width;
1161 gint height = overlay->image_height;
1167 if (xpos + width > overlay->width) {
1168 width = overlay->width - xpos;
1171 if (ypos + height > overlay->height) {
1172 height = overlay->height - ypos;
1175 dest += (ypos / 1) * dest_stride;
1177 for (i = 0; i < height; i++) {
1178 pimage = text_image + 4 * (i * overlay->image_width);
1179 py = dest + i * dest_stride + xpos;
1180 for (j = 0; j < width; j++) {
1181 b = pimage[CAIRO_ARGB_B];
1182 g = pimage[CAIRO_ARGB_G];
1183 r = pimage[CAIRO_ARGB_R];
1184 a = pimage[CAIRO_ARGB_A];
1185 CAIRO_UNPREMULTIPLY (a, r, g, b);
1192 COMP_Y (y, r, g, b);
1194 BLEND (*py++, a, y, x);
1200 gst_base_text_overlay_blit_sub2x2cbcr (GstBaseTextOverlay * overlay,
1201 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1202 guint destcb_stride, guint destcr_stride, guint pix_stride)
1207 gushort r1, g1, b1, a1;
1208 guchar *pimage1, *pimage2;
1210 gint width = overlay->image_width - 2;
1211 gint height = overlay->image_height - 2;
1219 if (xpos + width > overlay->width) {
1220 width = overlay->width - xpos;
1223 if (ypos + height > overlay->height) {
1224 height = overlay->height - ypos;
1227 destcb += (ypos / 2) * destcb_stride;
1228 destcr += (ypos / 2) * destcr_stride;
1230 for (i = 0; i < height; i += 2) {
1231 pimage1 = text_image + 4 * (i * overlay->image_width);
1232 pimage2 = pimage1 + 4 * overlay->image_width;
1233 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1234 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1235 for (j = 0; j < width; j += 2) {
1236 b = pimage1[CAIRO_ARGB_B];
1237 g = pimage1[CAIRO_ARGB_G];
1238 r = pimage1[CAIRO_ARGB_R];
1239 a = pimage1[CAIRO_ARGB_A];
1240 CAIRO_UNPREMULTIPLY (a, r, g, b);
1243 b1 = pimage1[CAIRO_ARGB_B];
1244 g1 = pimage1[CAIRO_ARGB_G];
1245 r1 = pimage1[CAIRO_ARGB_R];
1246 a1 = pimage1[CAIRO_ARGB_A];
1247 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1254 b1 = pimage2[CAIRO_ARGB_B];
1255 g1 = pimage2[CAIRO_ARGB_G];
1256 r1 = pimage2[CAIRO_ARGB_R];
1257 a1 = pimage2[CAIRO_ARGB_A];
1258 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1265 /* + 2 for rounding */
1266 b1 = pimage2[CAIRO_ARGB_B];
1267 g1 = pimage2[CAIRO_ARGB_G];
1268 r1 = pimage2[CAIRO_ARGB_R];
1269 a1 = pimage2[CAIRO_ARGB_A];
1270 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1287 COMP_U (cb, r, g, b);
1288 COMP_V (cr, r, g, b);
1291 BLEND (*pcb, a, cb, x);
1293 BLEND (*pcr, a, cr, x);
1302 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
1303 const gchar * string, gint textlen)
1306 cairo_surface_t *surface;
1307 PangoRectangle ink_rect, logical_rect;
1308 cairo_matrix_t cairo_matrix;
1310 double scalef = 1.0;
1313 g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1315 if (overlay->auto_adjust_size) {
1316 /* 640 pixel is default */
1317 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1319 pango_layout_set_width (overlay->layout, -1);
1320 /* set text on pango layout */
1321 pango_layout_set_markup (overlay->layout, string, textlen);
1323 /* get subtitle image size */
1324 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1326 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1328 if (width + overlay->deltax >
1329 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1331 * subtitle image width is larger then overlay width
1332 * so rearrange overlay wrap mode.
1334 gst_base_text_overlay_update_wrap_mode (overlay);
1335 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1336 width = overlay->width;
1340 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1341 if (height > overlay->height) {
1342 height = overlay->height;
1344 if (overlay->use_vertical_render) {
1345 PangoRectangle rect;
1346 PangoContext *context;
1347 PangoMatrix matrix = PANGO_MATRIX_INIT;
1350 context = pango_layout_get_context (overlay->layout);
1352 pango_matrix_rotate (&matrix, -90);
1354 rect.x = rect.y = 0;
1356 rect.height = height;
1357 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1358 matrix.x0 = -rect.x;
1359 matrix.y0 = -rect.y;
1361 pango_context_set_matrix (context, &matrix);
1363 cairo_matrix.xx = matrix.xx;
1364 cairo_matrix.yx = matrix.yx;
1365 cairo_matrix.xy = matrix.xy;
1366 cairo_matrix.yy = matrix.yy;
1367 cairo_matrix.x0 = matrix.x0;
1368 cairo_matrix.y0 = matrix.y0;
1369 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1375 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1378 /* reallocate surface */
1379 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1381 surface = cairo_image_surface_create_for_data (overlay->text_image,
1382 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1383 cr = cairo_create (surface);
1386 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1389 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1391 if (overlay->want_shading)
1392 cairo_paint_with_alpha (cr, overlay->shading_value);
1394 /* apply transformations */
1395 cairo_set_matrix (cr, &cairo_matrix);
1397 /* FIXME: We use show_layout everywhere except for the surface
1398 * because it's really faster and internally does all kinds of
1399 * caching. Unfortunately we have to paint to a cairo path for
1400 * the outline and this is slow. Once Pango supports user fonts
1401 * we should use them, see
1402 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1404 * Idea would the be, to create a cairo user font that
1405 * does shadow, outline, text painting in the
1406 * render_glyph function.
1409 /* draw shadow text */
1411 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1412 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1413 pango_cairo_show_layout (cr, overlay->layout);
1416 a = (overlay->outline_color >> 24) & 0xff;
1417 r = (overlay->outline_color >> 16) & 0xff;
1418 g = (overlay->outline_color >> 8) & 0xff;
1419 b = (overlay->outline_color >> 0) & 0xff;
1421 /* draw outline text */
1423 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1424 cairo_set_line_width (cr, overlay->outline_offset);
1425 pango_cairo_layout_path (cr, overlay->layout);
1429 a = (overlay->color >> 24) & 0xff;
1430 r = (overlay->color >> 16) & 0xff;
1431 g = (overlay->color >> 8) & 0xff;
1432 b = (overlay->color >> 0) & 0xff;
1436 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1437 pango_cairo_show_layout (cr, overlay->layout);
1441 cairo_surface_destroy (surface);
1442 overlay->image_width = width;
1443 overlay->image_height = height;
1444 overlay->baseline_y = ink_rect.y;
1445 g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1452 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
1453 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1455 gint i, j, dest_stride;
1458 dest_stride = dest->info.stride[0];
1459 dest_ptr = dest->data[0];
1461 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1462 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1464 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1465 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1467 for (i = y0; i < y1; ++i) {
1468 for (j = x0; j < x1; ++j) {
1469 gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
1471 dest_ptr[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1477 gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
1478 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1481 guint dest_stride, pixel_stride;
1484 dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
1485 dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
1486 pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
1488 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1489 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1491 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1492 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1495 x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
1497 x1 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x1);
1500 y0 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y0);
1502 y1 = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (dest->info.finfo, 0, y1);
1504 for (i = y0; i < y1; i++) {
1505 for (j = x0; j < x1; j++) {
1509 y_pos = (i * dest_stride) + j * pixel_stride;
1510 y = dest_ptr[y_pos] + overlay->shading_value;
1512 dest_ptr[y_pos] = CLAMP (y, 0, 255);
1517 #define gst_base_text_overlay_shade_BGRx gst_base_text_overlay_shade_xRGB
1518 #define gst_base_text_overlay_shade_RGBx gst_base_text_overlay_shade_xRGB
1519 #define gst_base_text_overlay_shade_xBGR gst_base_text_overlay_shade_xRGB
1521 gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
1522 GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
1527 dest_ptr = dest->data[0];
1529 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1530 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1532 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1533 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1535 for (i = y0; i < y1; i++) {
1536 for (j = x0; j < x1; j++) {
1539 y_pos = (i * 4 * overlay->width) + j * 4;
1540 for (k = 0; k < 4; k++) {
1541 y = dest_ptr[y_pos + k] + overlay->shading_value;
1542 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);
1548 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1549 static inline void \
1550 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
1551 gint x0, gint x1, gint y0, gint y1) \
1556 dest_ptr = dest->data[0];\
1558 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1559 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1561 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1562 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1564 for (i = y0; i < y1; i++) {\
1565 for (j = x0; j < x1; j++) {\
1567 y_pos = (i * 4 * overlay->width) + j * 4;\
1568 for (k = OFFSET; k < 3+OFFSET; k++) {\
1569 y = dest_ptr[y_pos + k] + overlay->shading_value;\
1570 dest_ptr[y_pos + k] = CLAMP (y, 0, 255);\
1575 ARGB_SHADE_FUNCTION (ARGB, 1);
1576 ARGB_SHADE_FUNCTION (ABGR, 1);
1577 ARGB_SHADE_FUNCTION (RGBA, 0);
1578 ARGB_SHADE_FUNCTION (BGRA, 0);
1582 * - use proper strides and offset for I420
1583 * - don't draw over the edge of the picture (try a longer
1584 * text with a huge font size)
1588 gst_base_text_overlay_blit_NV12_NV21 (GstBaseTextOverlay * overlay,
1589 GstVideoFrame * dest, gint xpos, gint ypos)
1591 int y_stride, u_stride, v_stride;
1592 guint8 *y_pixels, *u_pixels, *v_pixels;
1594 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1595 * to a boundary of integer number of U/V pixels:
1597 xpos = GST_ROUND_UP_2 (xpos);
1598 ypos = GST_ROUND_UP_2 (ypos);
1600 y_pixels = dest->data[0];
1601 u_pixels = dest->data[1];
1602 v_pixels = dest->data[2];
1603 y_stride = dest->info.stride[0];
1604 u_stride = dest->info.stride[1];
1605 v_stride = dest->info.stride[2];
1607 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1608 overlay->text_image, y_stride);
1609 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1610 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 2);
1614 gst_base_text_overlay_blit_I420 (GstBaseTextOverlay * overlay,
1615 GstVideoFrame * dest, gint xpos, gint ypos)
1617 int y_stride, u_stride, v_stride;
1618 guint8 *y_pixels, *u_pixels, *v_pixels;
1620 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1621 * to a boundary of integer number of U/V pixels:
1623 xpos = GST_ROUND_UP_2 (xpos);
1624 ypos = GST_ROUND_UP_2 (ypos);
1626 y_pixels = dest->data[0];
1627 u_pixels = dest->data[1];
1628 v_pixels = dest->data[2];
1629 y_stride = dest->info.stride[0];
1630 u_stride = dest->info.stride[1];
1631 v_stride = dest->info.stride[2];
1633 gst_base_text_overlay_blit_1 (overlay, y_pixels, xpos, ypos,
1634 overlay->text_image, y_stride);
1635 gst_base_text_overlay_blit_sub2x2cbcr (overlay, u_pixels,
1636 v_pixels, xpos, ypos, overlay->text_image, u_stride, v_stride, 1);
1640 gst_base_text_overlay_blit_UYVY (GstBaseTextOverlay * overlay,
1641 GstVideoFrame * dest, gint xpos, gint ypos)
1648 guchar *pimage, *dest_ptr;
1651 yuv_pixels = dest->data[0];
1653 /* because U/V is 2x horizontally subsampled, we need to round to a
1654 * boundary of integer number of U/V pixels in x dimension:
1656 xpos = GST_ROUND_UP_2 (xpos);
1658 w = overlay->image_width - 2;
1659 h = overlay->image_height - 2;
1665 if (xpos + w > overlay->width) {
1666 w = overlay->width - xpos;
1669 if (ypos + h > overlay->height) {
1670 h = overlay->height - ypos;
1673 for (i = 0; i < h; i++) {
1674 pimage = overlay->text_image + i * overlay->image_width * 4;
1675 dest_ptr = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1676 for (j = 0; j < w; j += 2) {
1677 b0 = pimage[CAIRO_ARGB_B];
1678 g0 = pimage[CAIRO_ARGB_G];
1679 r0 = pimage[CAIRO_ARGB_R];
1680 a0 = pimage[CAIRO_ARGB_A];
1681 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1684 b1 = pimage[CAIRO_ARGB_B];
1685 g1 = pimage[CAIRO_ARGB_G];
1686 r1 = pimage[CAIRO_ARGB_R];
1687 a1 = pimage[CAIRO_ARGB_A];
1688 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1698 COMP_Y (y0, r0, g0, b0);
1699 COMP_Y (y1, r1, g1, b1);
1709 COMP_U (u, r0, g0, b0);
1710 COMP_V (v, r0, g0, b0);
1712 BLEND (*dest_ptr, a0, u, *dest_ptr);
1714 BLEND (*dest_ptr, a0, y0, *dest_ptr);
1716 BLEND (*dest_ptr, a0, v, *dest_ptr);
1718 BLEND (*dest_ptr, a0, y1, *dest_ptr);
1725 gst_base_text_overlay_blit_AYUV (GstBaseTextOverlay * overlay,
1726 GstVideoFrame * dest, gint xpos, gint ypos)
1732 guchar *pimage, *dest_ptr;
1735 rgb_pixels = dest->data[0];
1737 w = overlay->image_width;
1738 h = overlay->image_height;
1744 if (xpos + w > overlay->width) {
1745 w = overlay->width - xpos;
1748 if (ypos + h > overlay->height) {
1749 h = overlay->height - ypos;
1752 for (i = 0; i < h; i++) {
1753 pimage = overlay->text_image + i * overlay->image_width * 4;
1754 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1755 for (j = 0; j < w; j++) {
1756 a = pimage[CAIRO_ARGB_A];
1757 b = pimage[CAIRO_ARGB_B];
1758 g = pimage[CAIRO_ARGB_G];
1759 r = pimage[CAIRO_ARGB_R];
1761 CAIRO_UNPREMULTIPLY (a, r, g, b);
1763 // convert background to yuv
1764 COMP_Y (y, r, g, b);
1765 COMP_U (u, r, g, b);
1766 COMP_V (v, r, g, b);
1768 // preform text "OVER" background alpha compositing
1769 a1 = a + (dest_ptr[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1770 OVER (dest_ptr[1], a, y, dest_ptr[0], dest_ptr[1], a1);
1771 OVER (dest_ptr[2], a, u, dest_ptr[0], dest_ptr[2], a1);
1772 OVER (dest_ptr[3], a, v, dest_ptr[0], dest_ptr[3], a1);
1773 dest_ptr[0] = a1 - 1; // remove the temporary 1 we added
1781 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1782 static inline void \
1783 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1784 GstVideoFrame * dest, gint xpos, gint ypos) \
1789 guchar *pimage, *dest_ptr; \
1790 guint8 *rgb_pixels;\
1792 rgb_pixels = dest->data[0];\
1794 w = overlay->image_width; \
1795 h = overlay->image_height; \
1801 if (xpos + w > overlay->width) { \
1802 w = overlay->width - xpos; \
1805 if (ypos + h > overlay->height) { \
1806 h = overlay->height - ypos; \
1809 for (i = 0; i < h; i++) { \
1810 pimage = overlay->text_image + i * overlay->image_width * 4; \
1811 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1812 for (j = 0; j < w; j++) { \
1813 a = pimage[CAIRO_ARGB_A]; \
1814 b = pimage[CAIRO_ARGB_B]; \
1815 g = pimage[CAIRO_ARGB_G]; \
1816 r = pimage[CAIRO_ARGB_R]; \
1817 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1818 b = (b*a + dest_ptr[B] * (255-a)) / 255; \
1819 g = (g*a + dest_ptr[G] * (255-a)) / 255; \
1820 r = (r*a + dest_ptr[R] * (255-a)) / 255; \
1830 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1831 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1832 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1833 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1835 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1836 static inline void \
1837 gst_base_text_overlay_blit_##name (GstBaseTextOverlay * overlay, \
1838 GstVideoFrame * dest, gint xpos, gint ypos) \
1840 int a, r, g, b, a1; \
1843 guchar *pimage, *dest_ptr; \
1844 guint8 *rgb_pixels;\
1846 rgb_pixels = dest->data[0];\
1848 w = overlay->image_width; \
1849 h = overlay->image_height; \
1855 if (xpos + w > overlay->width) { \
1856 w = overlay->width - xpos; \
1859 if (ypos + h > overlay->height) { \
1860 h = overlay->height - ypos; \
1863 for (i = 0; i < h; i++) { \
1864 pimage = overlay->text_image + i * overlay->image_width * 4; \
1865 dest_ptr = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1866 for (j = 0; j < w; j++) { \
1867 a = pimage[CAIRO_ARGB_A]; \
1868 b = pimage[CAIRO_ARGB_B]; \
1869 g = pimage[CAIRO_ARGB_G]; \
1870 r = pimage[CAIRO_ARGB_R]; \
1871 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1872 a1 = a + (dest_ptr[A] * (255 - a)) / 255 + 1; \
1873 OVER (dest_ptr[R], a, r, dest_ptr[0], dest_ptr[R], a1); \
1874 OVER (dest_ptr[G], a, g, dest_ptr[0], dest_ptr[G], a1); \
1875 OVER (dest_ptr[B], a, b, dest_ptr[0], dest_ptr[B], a1); \
1876 dest_ptr[A] = a1 - 1; \
1882 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1883 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1884 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1885 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1888 gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
1889 const gchar * text, gint textlen)
1893 if (!overlay->need_render) {
1894 GST_DEBUG ("Using previously rendered text.");
1898 /* -1 is the whole string */
1899 if (text != NULL && textlen < 0) {
1900 textlen = strlen (text);
1904 string = g_strndup (text, textlen);
1905 } else { /* empty string */
1906 string = g_strdup (" ");
1908 g_strdelimit (string, "\r\t", ' ');
1909 textlen = strlen (string);
1911 /* FIXME: should we check for UTF-8 here? */
1913 GST_DEBUG ("Rendering '%s'", string);
1914 gst_base_text_overlay_render_pangocairo (overlay, string, textlen);
1918 overlay->need_render = FALSE;
1921 static GstFlowReturn
1922 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
1923 GstBuffer * video_frame)
1927 GstBaseTextOverlayVAlign valign;
1928 GstBaseTextOverlayHAlign halign;
1929 GstVideoFrame frame;
1931 width = overlay->image_width;
1932 height = overlay->image_height;
1934 video_frame = gst_buffer_make_writable (video_frame);
1936 if (!gst_video_frame_map (&frame, &overlay->info, video_frame, GST_MAP_WRITE))
1939 if (overlay->use_vertical_render)
1940 halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
1942 halign = overlay->halign;
1945 case GST_BASE_TEXT_OVERLAY_HALIGN_LEFT:
1946 xpos = overlay->xpad;
1948 case GST_BASE_TEXT_OVERLAY_HALIGN_CENTER:
1949 xpos = (overlay->width - width) / 2;
1951 case GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT:
1952 xpos = overlay->width - width - overlay->xpad;
1954 case GST_BASE_TEXT_OVERLAY_HALIGN_POS:
1955 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1956 xpos = CLAMP (xpos, 0, overlay->width - width);
1963 xpos += overlay->deltax;
1965 if (overlay->use_vertical_render)
1966 valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
1968 valign = overlay->valign;
1971 case GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM:
1972 ypos = overlay->height - height - overlay->ypad;
1974 case GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE:
1975 ypos = overlay->height - (height + overlay->ypad);
1977 case GST_BASE_TEXT_OVERLAY_VALIGN_TOP:
1978 ypos = overlay->ypad;
1980 case GST_BASE_TEXT_OVERLAY_VALIGN_POS:
1981 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1982 ypos = CLAMP (ypos, 0, overlay->height - height);
1984 case GST_BASE_TEXT_OVERLAY_VALIGN_CENTER:
1985 ypos = (overlay->height - height) / 2;
1988 ypos = overlay->ypad;
1991 ypos += overlay->deltay;
1993 /* shaded background box */
1994 if (overlay->want_shading) {
1995 switch (overlay->format) {
1996 case GST_VIDEO_FORMAT_I420:
1997 case GST_VIDEO_FORMAT_NV12:
1998 case GST_VIDEO_FORMAT_NV21:
1999 gst_base_text_overlay_shade_planar_Y (overlay, &frame,
2000 xpos, xpos + overlay->image_width,
2001 ypos, ypos + overlay->image_height);
2003 case GST_VIDEO_FORMAT_AYUV:
2004 case GST_VIDEO_FORMAT_UYVY:
2005 gst_base_text_overlay_shade_packed_Y (overlay, &frame,
2006 xpos, xpos + overlay->image_width,
2007 ypos, ypos + overlay->image_height);
2009 case GST_VIDEO_FORMAT_xRGB:
2010 gst_base_text_overlay_shade_xRGB (overlay, &frame,
2011 xpos, xpos + overlay->image_width,
2012 ypos, ypos + overlay->image_height);
2014 case GST_VIDEO_FORMAT_xBGR:
2015 gst_base_text_overlay_shade_xBGR (overlay, &frame,
2016 xpos, xpos + overlay->image_width,
2017 ypos, ypos + overlay->image_height);
2019 case GST_VIDEO_FORMAT_BGRx:
2020 gst_base_text_overlay_shade_BGRx (overlay, &frame,
2021 xpos, xpos + overlay->image_width,
2022 ypos, ypos + overlay->image_height);
2024 case GST_VIDEO_FORMAT_RGBx:
2025 gst_base_text_overlay_shade_RGBx (overlay, &frame,
2026 xpos, xpos + overlay->image_width,
2027 ypos, ypos + overlay->image_height);
2029 case GST_VIDEO_FORMAT_ARGB:
2030 gst_base_text_overlay_shade_ARGB (overlay, &frame,
2031 xpos, xpos + overlay->image_width,
2032 ypos, ypos + overlay->image_height);
2034 case GST_VIDEO_FORMAT_ABGR:
2035 gst_base_text_overlay_shade_ABGR (overlay, &frame,
2036 xpos, xpos + overlay->image_width,
2037 ypos, ypos + overlay->image_height);
2039 case GST_VIDEO_FORMAT_RGBA:
2040 gst_base_text_overlay_shade_RGBA (overlay, &frame,
2041 xpos, xpos + overlay->image_width,
2042 ypos, ypos + overlay->image_height);
2044 case GST_VIDEO_FORMAT_BGRA:
2045 gst_base_text_overlay_shade_BGRA (overlay, &frame,
2046 xpos, xpos + overlay->image_width,
2047 ypos, ypos + overlay->image_height);
2050 g_assert_not_reached ();
2057 if (overlay->text_image) {
2058 switch (overlay->format) {
2059 case GST_VIDEO_FORMAT_I420:
2060 gst_base_text_overlay_blit_I420 (overlay, &frame, xpos, ypos);
2062 case GST_VIDEO_FORMAT_NV12:
2063 case GST_VIDEO_FORMAT_NV21:
2064 gst_base_text_overlay_blit_NV12_NV21 (overlay, &frame, xpos, ypos);
2066 case GST_VIDEO_FORMAT_UYVY:
2067 gst_base_text_overlay_blit_UYVY (overlay, &frame, xpos, ypos);
2069 case GST_VIDEO_FORMAT_AYUV:
2070 gst_base_text_overlay_blit_AYUV (overlay, &frame, xpos, ypos);
2072 case GST_VIDEO_FORMAT_BGRx:
2073 gst_base_text_overlay_blit_BGRx (overlay, &frame, xpos, ypos);
2075 case GST_VIDEO_FORMAT_xRGB:
2076 gst_base_text_overlay_blit_xRGB (overlay, &frame, xpos, ypos);
2078 case GST_VIDEO_FORMAT_RGBx:
2079 gst_base_text_overlay_blit_RGBx (overlay, &frame, xpos, ypos);
2081 case GST_VIDEO_FORMAT_xBGR:
2082 gst_base_text_overlay_blit_xBGR (overlay, &frame, xpos, ypos);
2084 case GST_VIDEO_FORMAT_ARGB:
2085 gst_base_text_overlay_blit_ARGB (overlay, &frame, xpos, ypos);
2087 case GST_VIDEO_FORMAT_ABGR:
2088 gst_base_text_overlay_blit_ABGR (overlay, &frame, xpos, ypos);
2090 case GST_VIDEO_FORMAT_RGBA:
2091 gst_base_text_overlay_blit_RGBA (overlay, &frame, xpos, ypos);
2093 case GST_VIDEO_FORMAT_BGRA:
2094 gst_base_text_overlay_blit_BGRA (overlay, &frame, xpos, ypos);
2097 g_assert_not_reached ();
2100 gst_video_frame_unmap (&frame);
2102 return gst_pad_push (overlay->srcpad, video_frame);
2107 gst_buffer_unref (video_frame);
2108 GST_DEBUG_OBJECT (overlay, "received invalid buffer");
2113 static GstPadLinkReturn
2114 gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2116 GstBaseTextOverlay *overlay;
2118 overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
2119 if (G_UNLIKELY (!overlay))
2120 return GST_PAD_LINK_REFUSED;
2122 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2124 overlay->text_linked = TRUE;
2126 gst_object_unref (overlay);
2128 return GST_PAD_LINK_OK;
2132 gst_base_text_overlay_text_pad_unlink (GstPad * pad)
2134 GstBaseTextOverlay *overlay;
2136 /* don't use gst_pad_get_parent() here, will deadlock */
2137 overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2139 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2141 overlay->text_linked = FALSE;
2143 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2147 gst_base_text_overlay_text_event (GstPad * pad, GstObject * parent,
2150 gboolean ret = FALSE;
2151 GstBaseTextOverlay *overlay = NULL;
2153 overlay = GST_BASE_TEXT_OVERLAY (parent);
2155 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2157 switch (GST_EVENT_TYPE (event)) {
2158 case GST_EVENT_CAPS:
2162 gst_event_parse_caps (event, &caps);
2163 ret = gst_base_text_overlay_setcaps_txt (overlay, caps);
2164 gst_event_unref (event);
2167 case GST_EVENT_SEGMENT:
2169 const GstSegment *segment;
2171 overlay->text_eos = FALSE;
2173 gst_event_parse_segment (event, &segment);
2175 if (segment->format == GST_FORMAT_TIME) {
2176 GST_OBJECT_LOCK (overlay);
2177 gst_segment_copy_into (segment, &overlay->text_segment);
2178 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2179 &overlay->text_segment);
2180 GST_OBJECT_UNLOCK (overlay);
2182 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2183 ("received non-TIME newsegment event on text input"));
2186 gst_event_unref (event);
2189 /* wake up the video chain, it might be waiting for a text buffer or
2190 * a text segment update */
2191 GST_OBJECT_LOCK (overlay);
2192 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2193 GST_OBJECT_UNLOCK (overlay);
2196 case GST_EVENT_FLUSH_STOP:
2197 GST_OBJECT_LOCK (overlay);
2198 GST_INFO_OBJECT (overlay, "text flush stop");
2199 overlay->text_flushing = FALSE;
2200 overlay->text_eos = FALSE;
2201 gst_base_text_overlay_pop_text (overlay);
2202 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2203 GST_OBJECT_UNLOCK (overlay);
2204 gst_event_unref (event);
2207 case GST_EVENT_FLUSH_START:
2208 GST_OBJECT_LOCK (overlay);
2209 GST_INFO_OBJECT (overlay, "text flush start");
2210 overlay->text_flushing = TRUE;
2211 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2212 GST_OBJECT_UNLOCK (overlay);
2213 gst_event_unref (event);
2217 GST_OBJECT_LOCK (overlay);
2218 overlay->text_eos = TRUE;
2219 GST_INFO_OBJECT (overlay, "text EOS");
2220 /* wake up the video chain, it might be waiting for a text buffer or
2221 * a text segment update */
2222 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2223 GST_OBJECT_UNLOCK (overlay);
2224 gst_event_unref (event);
2228 ret = gst_pad_event_default (pad, parent, event);
2236 gst_base_text_overlay_video_event (GstPad * pad, GstObject * parent,
2239 gboolean ret = FALSE;
2240 GstBaseTextOverlay *overlay = NULL;
2242 overlay = GST_BASE_TEXT_OVERLAY (parent);
2244 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2246 switch (GST_EVENT_TYPE (event)) {
2247 case GST_EVENT_CAPS:
2251 gst_event_parse_caps (event, &caps);
2252 ret = gst_base_text_overlay_setcaps (overlay, caps);
2253 gst_event_unref (event);
2256 case GST_EVENT_SEGMENT:
2258 const GstSegment *segment;
2260 GST_DEBUG_OBJECT (overlay, "received new segment");
2262 gst_event_parse_segment (event, &segment);
2264 if (segment->format == GST_FORMAT_TIME) {
2265 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2268 gst_segment_copy_into (segment, &overlay->segment);
2270 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2271 ("received non-TIME newsegment event on video input"));
2274 ret = gst_pad_event_default (pad, parent, event);
2278 GST_OBJECT_LOCK (overlay);
2279 GST_INFO_OBJECT (overlay, "video EOS");
2280 overlay->video_eos = TRUE;
2281 GST_OBJECT_UNLOCK (overlay);
2282 ret = gst_pad_event_default (pad, parent, event);
2284 case GST_EVENT_FLUSH_START:
2285 GST_OBJECT_LOCK (overlay);
2286 GST_INFO_OBJECT (overlay, "video flush start");
2287 overlay->video_flushing = TRUE;
2288 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2289 GST_OBJECT_UNLOCK (overlay);
2290 ret = gst_pad_event_default (pad, parent, event);
2292 case GST_EVENT_FLUSH_STOP:
2293 GST_OBJECT_LOCK (overlay);
2294 GST_INFO_OBJECT (overlay, "video flush stop");
2295 overlay->video_flushing = FALSE;
2296 overlay->video_eos = FALSE;
2297 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2298 GST_OBJECT_UNLOCK (overlay);
2299 ret = gst_pad_event_default (pad, parent, event);
2302 ret = gst_pad_event_default (pad, parent, event);
2310 gst_base_text_overlay_video_query (GstPad * pad, GstObject * parent,
2313 gboolean ret = FALSE;
2314 GstBaseTextOverlay *overlay;
2316 overlay = GST_BASE_TEXT_OVERLAY (parent);
2318 switch (GST_QUERY_TYPE (query)) {
2319 case GST_QUERY_CAPS:
2321 GstCaps *filter, *caps;
2323 gst_query_parse_caps (query, &filter);
2324 caps = gst_base_text_overlay_getcaps (pad, overlay, filter);
2325 gst_query_set_caps_result (query, caps);
2326 gst_caps_unref (caps);
2331 ret = gst_pad_query_default (pad, parent, query);
2338 /* Called with lock held */
2340 gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay)
2342 g_return_if_fail (GST_IS_BASE_TEXT_OVERLAY (overlay));
2344 if (overlay->text_buffer) {
2345 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2346 overlay->text_buffer);
2347 gst_buffer_unref (overlay->text_buffer);
2348 overlay->text_buffer = NULL;
2351 /* Let the text task know we used that buffer */
2352 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2355 /* We receive text buffers here. If they are out of segment we just ignore them.
2356 If the buffer is in our segment we keep it internally except if another one
2357 is already waiting here, in that case we wait that it gets kicked out */
2358 static GstFlowReturn
2359 gst_base_text_overlay_text_chain (GstPad * pad, GstObject * parent,
2362 GstFlowReturn ret = GST_FLOW_OK;
2363 GstBaseTextOverlay *overlay = NULL;
2364 gboolean in_seg = FALSE;
2365 guint64 clip_start = 0, clip_stop = 0;
2367 overlay = GST_BASE_TEXT_OVERLAY (parent);
2369 GST_OBJECT_LOCK (overlay);
2371 if (overlay->text_flushing) {
2372 GST_OBJECT_UNLOCK (overlay);
2373 ret = GST_FLOW_FLUSHING;
2374 GST_LOG_OBJECT (overlay, "text flushing");
2378 if (overlay->text_eos) {
2379 GST_OBJECT_UNLOCK (overlay);
2381 GST_LOG_OBJECT (overlay, "text EOS");
2385 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2386 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2387 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2388 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2389 GST_BUFFER_DURATION (buffer)));
2391 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2394 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2395 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2397 stop = GST_CLOCK_TIME_NONE;
2399 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2400 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2406 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2407 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2408 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2409 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2411 /* Wait for the previous buffer to go away */
2412 while (overlay->text_buffer != NULL) {
2413 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2414 GST_DEBUG_PAD_NAME (pad));
2415 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2416 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2417 if (overlay->text_flushing) {
2418 GST_OBJECT_UNLOCK (overlay);
2419 ret = GST_FLOW_FLUSHING;
2424 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2425 overlay->text_segment.position = clip_start;
2427 overlay->text_buffer = buffer;
2428 /* That's a new text buffer we need to render */
2429 overlay->need_render = TRUE;
2431 /* in case the video chain is waiting for a text buffer, wake it up */
2432 GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
2435 GST_OBJECT_UNLOCK (overlay);
2442 static GstFlowReturn
2443 gst_base_text_overlay_video_chain (GstPad * pad, GstObject * parent,
2446 GstBaseTextOverlayClass *klass;
2447 GstBaseTextOverlay *overlay;
2448 GstFlowReturn ret = GST_FLOW_OK;
2449 gboolean in_seg = FALSE;
2450 guint64 start, stop, clip_start = 0, clip_stop = 0;
2453 overlay = GST_BASE_TEXT_OVERLAY (parent);
2454 klass = GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay);
2456 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2457 goto missing_timestamp;
2459 /* ignore buffers that are outside of the current segment */
2460 start = GST_BUFFER_TIMESTAMP (buffer);
2462 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2463 stop = GST_CLOCK_TIME_NONE;
2465 stop = start + GST_BUFFER_DURATION (buffer);
2468 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2469 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2470 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2472 /* segment_clip() will adjust start unconditionally to segment_start if
2473 * no stop time is provided, so handle this ourselves */
2474 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2475 goto out_of_segment;
2477 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2478 &clip_start, &clip_stop);
2481 goto out_of_segment;
2483 /* if the buffer is only partially in the segment, fix up stamps */
2484 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2485 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2486 buffer = gst_buffer_make_writable (buffer);
2487 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2489 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2492 /* now, after we've done the clipping, fix up end time if there's no
2493 * duration (we only use those estimated values internally though, we
2494 * don't want to set bogus values on the buffer itself) */
2498 gint fps_num, fps_denom;
2500 /* FIXME, store this in setcaps */
2501 caps = gst_pad_get_current_caps (pad);
2502 s = gst_caps_get_structure (caps, 0);
2503 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2504 fps_num && fps_denom) {
2505 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2506 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2508 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2509 stop = start + 1; /* we need to assume some interval */
2511 gst_caps_unref (caps);
2514 gst_object_sync_values (GST_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2518 GST_OBJECT_LOCK (overlay);
2520 if (overlay->video_flushing)
2523 if (overlay->video_eos)
2526 if (overlay->silent) {
2527 GST_OBJECT_UNLOCK (overlay);
2528 ret = gst_pad_push (overlay->srcpad, buffer);
2530 /* Update position */
2531 overlay->segment.position = clip_start;
2536 /* Text pad not linked, rendering internal text */
2537 if (!overlay->text_linked) {
2538 if (klass->get_text) {
2539 text = klass->get_text (overlay, buffer);
2541 text = g_strdup (overlay->default_text);
2544 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2545 "text: '%s'", GST_STR_NULL (text));
2547 GST_OBJECT_UNLOCK (overlay);
2549 if (text != NULL && *text != '\0') {
2550 /* Render and push */
2551 gst_base_text_overlay_render_text (overlay, text, -1);
2552 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2554 /* Invalid or empty string */
2555 ret = gst_pad_push (overlay->srcpad, buffer);
2558 /* Text pad linked, check if we have a text buffer queued */
2559 if (overlay->text_buffer) {
2560 gboolean pop_text = FALSE, valid_text_time = TRUE;
2561 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2562 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2563 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2564 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2565 GstClockTime vid_running_time, vid_running_time_end;
2567 /* if the text buffer isn't stamped right, pop it off the
2568 * queue and display it for the current video frame only */
2569 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2570 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2571 GST_WARNING_OBJECT (overlay,
2572 "Got text buffer with invalid timestamp or duration");
2574 valid_text_time = FALSE;
2576 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2577 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2581 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2583 vid_running_time_end =
2584 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2587 /* If timestamp and duration are valid */
2588 if (valid_text_time) {
2590 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2592 text_running_time_end =
2593 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2597 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2598 GST_TIME_ARGS (text_running_time),
2599 GST_TIME_ARGS (text_running_time_end));
2600 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2601 GST_TIME_ARGS (vid_running_time),
2602 GST_TIME_ARGS (vid_running_time_end));
2604 /* Text too old or in the future */
2605 if (valid_text_time && text_running_time_end <= vid_running_time) {
2606 /* text buffer too old, get rid of it and do nothing */
2607 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2609 gst_base_text_overlay_pop_text (overlay);
2610 GST_OBJECT_UNLOCK (overlay);
2611 goto wait_for_text_buf;
2612 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2613 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2614 GST_OBJECT_UNLOCK (overlay);
2615 /* Push the video frame */
2616 ret = gst_pad_push (overlay->srcpad, buffer);
2622 gst_buffer_map (overlay->text_buffer, &map, GST_MAP_READ);
2623 in_text = (gchar *) map.data;
2626 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2627 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2628 * here on purpose, this is something that needs fixing upstream */
2629 if (!g_utf8_validate (in_text, in_size, NULL)) {
2630 const gchar *end = NULL;
2632 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2633 in_text = g_strndup (in_text, in_size);
2634 while (!g_utf8_validate (in_text, in_size, &end) && end)
2635 *((gchar *) end) = '*';
2638 /* Get the string */
2639 if (overlay->have_pango_markup) {
2640 text = g_strndup (in_text, in_size);
2642 text = g_markup_escape_text (in_text, in_size);
2645 if (text != NULL && *text != '\0') {
2646 gint text_len = strlen (text);
2648 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2649 text[text_len - 1] == '\r')) {
2652 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2653 gst_base_text_overlay_render_text (overlay, text, text_len);
2655 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2656 gst_base_text_overlay_render_text (overlay, " ", 1);
2658 if (in_text != (gchar *) map.data)
2661 gst_buffer_unmap (overlay->text_buffer, &map);
2663 GST_OBJECT_UNLOCK (overlay);
2664 ret = gst_base_text_overlay_push_frame (overlay, buffer);
2666 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2667 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2672 GST_OBJECT_LOCK (overlay);
2673 gst_base_text_overlay_pop_text (overlay);
2674 GST_OBJECT_UNLOCK (overlay);
2677 gboolean wait_for_text_buf = TRUE;
2679 if (overlay->text_eos)
2680 wait_for_text_buf = FALSE;
2682 if (!overlay->wait_text)
2683 wait_for_text_buf = FALSE;
2685 /* Text pad linked, but no text buffer available - what now? */
2686 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2687 GstClockTime text_start_running_time, text_position_running_time;
2688 GstClockTime vid_running_time;
2691 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2692 GST_BUFFER_TIMESTAMP (buffer));
2693 text_start_running_time =
2694 gst_segment_to_running_time (&overlay->text_segment,
2695 GST_FORMAT_TIME, overlay->text_segment.start);
2696 text_position_running_time =
2697 gst_segment_to_running_time (&overlay->text_segment,
2698 GST_FORMAT_TIME, overlay->text_segment.position);
2700 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2701 vid_running_time < text_start_running_time) ||
2702 (GST_CLOCK_TIME_IS_VALID (text_position_running_time) &&
2703 vid_running_time < text_position_running_time)) {
2704 wait_for_text_buf = FALSE;
2708 if (wait_for_text_buf) {
2709 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2710 GST_BASE_TEXT_OVERLAY_WAIT (overlay);
2711 GST_DEBUG_OBJECT (overlay, "resuming");
2712 GST_OBJECT_UNLOCK (overlay);
2713 goto wait_for_text_buf;
2715 GST_OBJECT_UNLOCK (overlay);
2716 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2717 ret = gst_pad_push (overlay->srcpad, buffer);
2724 /* Update position */
2725 overlay->segment.position = clip_start;
2731 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2732 gst_buffer_unref (buffer);
2738 GST_OBJECT_UNLOCK (overlay);
2739 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2740 gst_buffer_unref (buffer);
2741 return GST_FLOW_FLUSHING;
2745 GST_OBJECT_UNLOCK (overlay);
2746 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2747 gst_buffer_unref (buffer);
2748 return GST_FLOW_EOS;
2752 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2753 gst_buffer_unref (buffer);
2758 static GstStateChangeReturn
2759 gst_base_text_overlay_change_state (GstElement * element,
2760 GstStateChange transition)
2762 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2763 GstBaseTextOverlay *overlay = GST_BASE_TEXT_OVERLAY (element);
2765 switch (transition) {
2766 case GST_STATE_CHANGE_PAUSED_TO_READY:
2767 GST_OBJECT_LOCK (overlay);
2768 overlay->text_flushing = TRUE;
2769 overlay->video_flushing = TRUE;
2770 /* pop_text will broadcast on the GCond and thus also make the video
2771 * chain exit if it's waiting for a text buffer */
2772 gst_base_text_overlay_pop_text (overlay);
2773 GST_OBJECT_UNLOCK (overlay);
2779 ret = parent_class->change_state (element, transition);
2780 if (ret == GST_STATE_CHANGE_FAILURE)
2783 switch (transition) {
2784 case GST_STATE_CHANGE_READY_TO_PAUSED:
2785 GST_OBJECT_LOCK (overlay);
2786 overlay->text_flushing = FALSE;
2787 overlay->video_flushing = FALSE;
2788 overlay->video_eos = FALSE;
2789 overlay->text_eos = FALSE;
2790 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2791 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2792 GST_OBJECT_UNLOCK (overlay);
2802 plugin_init (GstPlugin * plugin)
2804 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2805 GST_TYPE_TEXT_OVERLAY) ||
2806 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2807 GST_TYPE_TIME_OVERLAY) ||
2808 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2809 GST_TYPE_CLOCK_OVERLAY) ||
2810 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2811 GST_TYPE_TEXT_RENDER)) {
2815 /*texttestsrc_plugin_init(module, plugin); */
2817 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2822 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2823 "pango", "Pango-based text rendering and overlay", plugin_init,
2824 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)