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 "gsttextoverlay.h"
81 #include "gsttimeoverlay.h"
82 #include "gstclockoverlay.h"
83 #include "gsttextrender.h"
87 * - use proper strides and offset for I420
88 * - if text is wider than the video picture, it does not get
89 * clipped properly during blitting (if wrapping is disabled)
90 * - make 'shading_value' a property (or enum: light/normal/dark/verydark)?
93 GST_DEBUG_CATEGORY (pango_debug);
94 #define GST_CAT_DEFAULT pango_debug
96 #define DEFAULT_PROP_TEXT ""
97 #define DEFAULT_PROP_SHADING FALSE
98 #define DEFAULT_PROP_VALIGNMENT GST_TEXT_OVERLAY_VALIGN_BASELINE
99 #define DEFAULT_PROP_HALIGNMENT GST_TEXT_OVERLAY_HALIGN_CENTER
100 #define DEFAULT_PROP_VALIGN "baseline"
101 #define DEFAULT_PROP_HALIGN "center"
102 #define DEFAULT_PROP_XPAD 25
103 #define DEFAULT_PROP_YPAD 25
104 #define DEFAULT_PROP_DELTAX 0
105 #define DEFAULT_PROP_DELTAY 0
106 #define DEFAULT_PROP_XPOS 0.5
107 #define DEFAULT_PROP_YPOS 0.5
108 #define DEFAULT_PROP_WRAP_MODE GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR
109 #define DEFAULT_PROP_FONT_DESC ""
110 #define DEFAULT_PROP_SILENT FALSE
111 #define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER
112 #define DEFAULT_PROP_WAIT_TEXT TRUE
113 #define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE
114 #define DEFAULT_PROP_VERTICAL_RENDER FALSE
115 #define DEFAULT_PROP_COLOR 0xffffffff
117 /* make a property of me */
118 #define DEFAULT_SHADING_VALUE -80
120 #define MINIMUM_OUTLINE_OFFSET 1.0
121 #define DEFAULT_SCALE_BASIS 640
123 #define COMP_Y(ret, r, g, b) \
125 ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \
126 ret = CLAMP (ret, 0, 255); \
129 #define COMP_U(ret, r, g, b) \
131 ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \
132 ret = CLAMP (ret, 0, 255); \
135 #define COMP_V(ret, r, g, b) \
137 ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \
138 ret = CLAMP (ret, 0, 255); \
141 #define BLEND(ret, alpha, v0, v1) \
143 ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \
146 #define OVER(ret, alphaA, Ca, alphaB, Cb, alphaNew) \
149 _tmp = (Ca * alphaA + Cb * alphaB * (255 - alphaA) / 255) / alphaNew; \
150 ret = CLAMP (_tmp, 0, 255); \
153 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
154 # define CAIRO_ARGB_A 3
155 # define CAIRO_ARGB_R 2
156 # define CAIRO_ARGB_G 1
157 # define CAIRO_ARGB_B 0
159 # define CAIRO_ARGB_A 0
160 # define CAIRO_ARGB_R 1
161 # define CAIRO_ARGB_G 2
162 # define CAIRO_ARGB_B 3
170 PROP_VALIGN, /* deprecated */
171 PROP_HALIGN, /* deprecated */
185 PROP_AUTO_ADJUST_SIZE,
186 PROP_VERTICAL_RENDER,
191 static GstStaticPadTemplate src_template_factory =
192 GST_STATIC_PAD_TEMPLATE ("src",
195 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";"
196 GST_VIDEO_CAPS_RGBx ";"
197 GST_VIDEO_CAPS_xRGB ";"
198 GST_VIDEO_CAPS_xBGR ";"
199 GST_VIDEO_CAPS_RGBA ";"
200 GST_VIDEO_CAPS_BGRA ";"
201 GST_VIDEO_CAPS_ARGB ";"
202 GST_VIDEO_CAPS_ABGR ";"
203 GST_VIDEO_CAPS_YUV ("{AYUV, I420, UYVY, NV12, NV21}"))
206 static GstStaticPadTemplate video_sink_template_factory =
207 GST_STATIC_PAD_TEMPLATE ("video_sink",
210 GST_STATIC_CAPS (GST_VIDEO_CAPS_BGRx ";"
211 GST_VIDEO_CAPS_RGBx ";"
212 GST_VIDEO_CAPS_xRGB ";"
213 GST_VIDEO_CAPS_xBGR ";"
214 GST_VIDEO_CAPS_RGBA ";"
215 GST_VIDEO_CAPS_BGRA ";"
216 GST_VIDEO_CAPS_ARGB ";"
217 GST_VIDEO_CAPS_ABGR ";"
218 GST_VIDEO_CAPS_YUV ("{AYUV, I420, UYVY, NV12, NV21}"))
221 static GstStaticPadTemplate text_sink_template_factory =
222 GST_STATIC_PAD_TEMPLATE ("text_sink",
225 GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
228 #define GST_TYPE_TEXT_OVERLAY_VALIGN (gst_text_overlay_valign_get_type())
230 gst_text_overlay_valign_get_type (void)
232 static GType text_overlay_valign_type = 0;
233 static const GEnumValue text_overlay_valign[] = {
234 {GST_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"},
235 {GST_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"},
236 {GST_TEXT_OVERLAY_VALIGN_TOP, "top", "top"},
237 {GST_TEXT_OVERLAY_VALIGN_POS, "position", "position"},
238 {GST_TEXT_OVERLAY_VALIGN_CENTER, "center", "center"},
242 if (!text_overlay_valign_type) {
243 text_overlay_valign_type =
244 g_enum_register_static ("GstTextOverlayVAlign", text_overlay_valign);
246 return text_overlay_valign_type;
249 #define GST_TYPE_TEXT_OVERLAY_HALIGN (gst_text_overlay_halign_get_type())
251 gst_text_overlay_halign_get_type (void)
253 static GType text_overlay_halign_type = 0;
254 static const GEnumValue text_overlay_halign[] = {
255 {GST_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"},
256 {GST_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"},
257 {GST_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"},
258 {GST_TEXT_OVERLAY_HALIGN_POS, "position", "position"},
262 if (!text_overlay_halign_type) {
263 text_overlay_halign_type =
264 g_enum_register_static ("GstTextOverlayHAlign", text_overlay_halign);
266 return text_overlay_halign_type;
270 #define GST_TYPE_TEXT_OVERLAY_WRAP_MODE (gst_text_overlay_wrap_mode_get_type())
272 gst_text_overlay_wrap_mode_get_type (void)
274 static GType text_overlay_wrap_mode_type = 0;
275 static const GEnumValue text_overlay_wrap_mode[] = {
276 {GST_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"},
277 {GST_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"},
278 {GST_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"},
279 {GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"},
283 if (!text_overlay_wrap_mode_type) {
284 text_overlay_wrap_mode_type =
285 g_enum_register_static ("GstTextOverlayWrapMode",
286 text_overlay_wrap_mode);
288 return text_overlay_wrap_mode_type;
291 #define GST_TYPE_TEXT_OVERLAY_LINE_ALIGN (gst_text_overlay_line_align_get_type())
293 gst_text_overlay_line_align_get_type (void)
295 static GType text_overlay_line_align_type = 0;
296 static const GEnumValue text_overlay_line_align[] = {
297 {GST_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"},
298 {GST_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"},
299 {GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"},
303 if (!text_overlay_line_align_type) {
304 text_overlay_line_align_type =
305 g_enum_register_static ("GstTextOverlayLineAlign",
306 text_overlay_line_align);
308 return text_overlay_line_align_type;
311 #define GST_TEXT_OVERLAY_GET_COND(ov) (((GstTextOverlay *)ov)->cond)
312 #define GST_TEXT_OVERLAY_WAIT(ov) (g_cond_wait (GST_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov)))
313 #define GST_TEXT_OVERLAY_SIGNAL(ov) (g_cond_signal (GST_TEXT_OVERLAY_GET_COND (ov)))
314 #define GST_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_TEXT_OVERLAY_GET_COND (ov)))
316 static GstStateChangeReturn gst_text_overlay_change_state (GstElement * element,
317 GstStateChange transition);
319 static GstCaps *gst_text_overlay_getcaps (GstPad * pad);
320 static gboolean gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps);
321 static gboolean gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps);
322 static gboolean gst_text_overlay_src_event (GstPad * pad, GstEvent * event);
323 static gboolean gst_text_overlay_src_query (GstPad * pad, GstQuery * query);
325 static gboolean gst_text_overlay_video_event (GstPad * pad, GstEvent * event);
326 static GstFlowReturn gst_text_overlay_video_chain (GstPad * pad,
328 static GstFlowReturn gst_text_overlay_video_bufferalloc (GstPad * pad,
329 guint64 offset, guint size, GstCaps * caps, GstBuffer ** buffer);
331 static gboolean gst_text_overlay_text_event (GstPad * pad, GstEvent * event);
332 static GstFlowReturn gst_text_overlay_text_chain (GstPad * pad,
334 static GstPadLinkReturn gst_text_overlay_text_pad_link (GstPad * pad,
336 static void gst_text_overlay_text_pad_unlink (GstPad * pad);
337 static void gst_text_overlay_pop_text (GstTextOverlay * overlay);
338 static void gst_text_overlay_update_render_mode (GstTextOverlay * overlay);
340 static void gst_text_overlay_finalize (GObject * object);
341 static void gst_text_overlay_set_property (GObject * object, guint prop_id,
342 const GValue * value, GParamSpec * pspec);
343 static void gst_text_overlay_get_property (GObject * object, guint prop_id,
344 GValue * value, GParamSpec * pspec);
345 static void gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay *
346 overlay, PangoFontDescription * desc);
348 GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement,
352 gst_text_overlay_base_init (gpointer g_class)
354 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
355 GstTextOverlayClass *klass = GST_TEXT_OVERLAY_CLASS (g_class);
356 PangoFontMap *fontmap;
358 gst_element_class_add_pad_template (element_class,
359 gst_static_pad_template_get (&src_template_factory));
360 gst_element_class_add_pad_template (element_class,
361 gst_static_pad_template_get (&video_sink_template_factory));
364 if (!GST_IS_TIME_OVERLAY_CLASS (g_class) &&
365 !GST_IS_CLOCK_OVERLAY_CLASS (g_class)) {
366 gst_element_class_add_pad_template (element_class,
367 gst_static_pad_template_get (&text_sink_template_factory));
370 gst_element_class_set_details_simple (element_class, "Text overlay",
371 "Filter/Editor/Video",
372 "Adds text strings on top of a video buffer",
373 "David Schleef <ds@schleef.org>, " "Zeeshan Ali <zeeshan.ali@nokia.com>");
375 /* Only lock for the subclasses here, the base class
376 * doesn't have this mutex yet and it's not necessary
378 if (klass->pango_lock)
379 g_mutex_lock (klass->pango_lock);
380 fontmap = pango_cairo_font_map_get_default ();
381 klass->pango_context =
382 pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
383 if (klass->pango_lock)
384 g_mutex_unlock (klass->pango_lock);
388 gst_text_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame)
390 return g_strdup (overlay->default_text);
394 gst_text_overlay_class_init (GstTextOverlayClass * klass)
396 GObjectClass *gobject_class;
397 GstElementClass *gstelement_class;
399 gobject_class = (GObjectClass *) klass;
400 gstelement_class = (GstElementClass *) klass;
402 gobject_class->finalize = gst_text_overlay_finalize;
403 gobject_class->set_property = gst_text_overlay_set_property;
404 gobject_class->get_property = gst_text_overlay_get_property;
406 gstelement_class->change_state =
407 GST_DEBUG_FUNCPTR (gst_text_overlay_change_state);
409 klass->pango_lock = g_mutex_new ();
411 klass->get_text = gst_text_overlay_get_text;
413 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
414 g_param_spec_string ("text", "text",
415 "Text to be display.", DEFAULT_PROP_TEXT,
416 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
417 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
418 g_param_spec_boolean ("shaded-background", "shaded background",
419 "Whether to shade the background under the text area",
420 DEFAULT_PROP_SHADING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
421 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
422 g_param_spec_enum ("valignment", "vertical alignment",
423 "Vertical alignment of the text", GST_TYPE_TEXT_OVERLAY_VALIGN,
424 DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
425 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
426 g_param_spec_enum ("halignment", "horizontal alignment",
427 "Horizontal alignment of the text", GST_TYPE_TEXT_OVERLAY_HALIGN,
428 DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
429 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
430 g_param_spec_string ("valign", "vertical alignment",
431 "Vertical alignment of the text (deprecated; use valignment)",
432 DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
433 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
434 g_param_spec_string ("halign", "horizontal alignment",
435 "Horizontal alignment of the text (deprecated; use halignment)",
436 DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
437 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
438 g_param_spec_int ("xpad", "horizontal paddding",
439 "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
440 DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
441 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
442 g_param_spec_int ("ypad", "vertical padding",
443 "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
444 DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
445 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
446 g_param_spec_int ("deltax", "X position modifier",
447 "Shift X position to the left or to the right. Unit is pixels.",
448 G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX,
449 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
450 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
451 g_param_spec_int ("deltay", "Y position modifier",
452 "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
453 DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
455 * GstTextOverlay:xpos
457 * Horizontal position of the rendered text when using positioned alignment.
461 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
462 g_param_spec_double ("xpos", "horizontal position",
463 "Horizontal position when using position alignment", 0, 1.0,
465 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
467 * GstTextOverlay:ypos
469 * Vertical position of the rendered text when using positioned alignment.
473 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
474 g_param_spec_double ("ypos", "vertical position",
475 "Vertical position when using position alignment", 0, 1.0,
477 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
478 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
479 g_param_spec_enum ("wrap-mode", "wrap mode",
480 "Whether to wrap the text and if so how.",
481 GST_TYPE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
482 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
483 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
484 g_param_spec_string ("font-desc", "font description",
485 "Pango font description of font to be used for rendering. "
486 "See documentation of pango_font_description_from_string "
487 "for syntax.", DEFAULT_PROP_FONT_DESC,
488 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
490 * GstTextOverlay:color
492 * Color of the rendered text.
496 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
497 g_param_spec_uint ("color", "Color",
498 "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
500 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
503 * GstTextOverlay:line-alignment
505 * Alignment of text lines relative to each other (for multi-line text)
509 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
510 g_param_spec_enum ("line-alignment", "line alignment",
511 "Alignment of text lines relative to each other.",
512 GST_TYPE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
513 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
515 * GstTextOverlay:silent
517 * If set, no text is rendered. Useful to switch off text rendering
518 * temporarily without removing the textoverlay element from the pipeline.
522 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
523 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
524 g_param_spec_boolean ("silent", "silent",
525 "Whether to render the text string",
527 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
529 * GstTextOverlay:wait-text
531 * If set, the video will block until a subtitle is received on the text pad.
532 * If video and subtitles are sent in sync, like from the same demuxer, this
533 * property should be set.
537 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
538 g_param_spec_boolean ("wait-text", "Wait Text",
539 "Whether to wait for subtitles",
540 DEFAULT_PROP_WAIT_TEXT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
542 g_object_class_install_property (G_OBJECT_CLASS (klass),
543 PROP_AUTO_ADJUST_SIZE, g_param_spec_boolean ("auto-resize", "auto resize",
544 "Automatically adjust font size to screen-size.",
545 DEFAULT_PROP_AUTO_ADJUST_SIZE,
546 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
548 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VERTICAL_RENDER,
549 g_param_spec_boolean ("vertical-render", "vertical render",
550 "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER,
551 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
555 gst_text_overlay_finalize (GObject * object)
557 GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
559 g_free (overlay->default_text);
561 if (overlay->text_image) {
562 g_free (overlay->text_image);
563 overlay->text_image = NULL;
566 if (overlay->layout) {
567 g_object_unref (overlay->layout);
568 overlay->layout = NULL;
571 if (overlay->text_buffer) {
572 gst_buffer_unref (overlay->text_buffer);
573 overlay->text_buffer = NULL;
577 g_cond_free (overlay->cond);
578 overlay->cond = NULL;
581 G_OBJECT_CLASS (parent_class)->finalize (object);
585 gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
587 GstPadTemplate *template;
588 PangoFontDescription *desc;
591 template = gst_static_pad_template_get (&video_sink_template_factory);
592 overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
593 gst_object_unref (template);
594 gst_pad_set_getcaps_function (overlay->video_sinkpad,
595 GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
596 gst_pad_set_setcaps_function (overlay->video_sinkpad,
597 GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps));
598 gst_pad_set_event_function (overlay->video_sinkpad,
599 GST_DEBUG_FUNCPTR (gst_text_overlay_video_event));
600 gst_pad_set_chain_function (overlay->video_sinkpad,
601 GST_DEBUG_FUNCPTR (gst_text_overlay_video_chain));
602 gst_pad_set_bufferalloc_function (overlay->video_sinkpad,
603 GST_DEBUG_FUNCPTR (gst_text_overlay_video_bufferalloc));
604 gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
606 if (!GST_IS_TIME_OVERLAY_CLASS (klass) && !GST_IS_CLOCK_OVERLAY_CLASS (klass)) {
608 template = gst_static_pad_template_get (&text_sink_template_factory);
609 overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
610 gst_object_unref (template);
611 gst_pad_set_setcaps_function (overlay->text_sinkpad,
612 GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps_txt));
613 gst_pad_set_event_function (overlay->text_sinkpad,
614 GST_DEBUG_FUNCPTR (gst_text_overlay_text_event));
615 gst_pad_set_chain_function (overlay->text_sinkpad,
616 GST_DEBUG_FUNCPTR (gst_text_overlay_text_chain));
617 gst_pad_set_link_function (overlay->text_sinkpad,
618 GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_link));
619 gst_pad_set_unlink_function (overlay->text_sinkpad,
620 GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_unlink));
621 gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
625 template = gst_static_pad_template_get (&src_template_factory);
626 overlay->srcpad = gst_pad_new_from_template (template, "src");
627 gst_object_unref (template);
628 gst_pad_set_getcaps_function (overlay->srcpad,
629 GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
630 gst_pad_set_event_function (overlay->srcpad,
631 GST_DEBUG_FUNCPTR (gst_text_overlay_src_event));
632 gst_pad_set_query_function (overlay->srcpad,
633 GST_DEBUG_FUNCPTR (gst_text_overlay_src_query));
634 gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
636 overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
637 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
639 pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context);
641 pango_context_get_font_description (GST_TEXT_OVERLAY_GET_CLASS
642 (overlay)->pango_context);
643 gst_text_overlay_adjust_values_with_fontdesc (overlay, desc);
645 overlay->color = DEFAULT_PROP_COLOR;
646 overlay->halign = DEFAULT_PROP_HALIGNMENT;
647 overlay->valign = DEFAULT_PROP_VALIGNMENT;
648 overlay->xpad = DEFAULT_PROP_XPAD;
649 overlay->ypad = DEFAULT_PROP_YPAD;
650 overlay->deltax = DEFAULT_PROP_DELTAX;
651 overlay->deltay = DEFAULT_PROP_DELTAY;
652 overlay->xpos = DEFAULT_PROP_XPOS;
653 overlay->ypos = DEFAULT_PROP_YPOS;
655 overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
657 overlay->want_shading = DEFAULT_PROP_SHADING;
658 overlay->shading_value = DEFAULT_SHADING_VALUE;
659 overlay->silent = DEFAULT_PROP_SILENT;
660 overlay->wait_text = DEFAULT_PROP_WAIT_TEXT;
661 overlay->auto_adjust_size = DEFAULT_PROP_AUTO_ADJUST_SIZE;
663 overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
664 overlay->need_render = TRUE;
665 overlay->text_image = NULL;
666 overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER;
667 gst_text_overlay_update_render_mode (overlay);
672 overlay->text_buffer = NULL;
673 overlay->text_linked = FALSE;
674 overlay->cond = g_cond_new ();
675 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
676 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
680 gst_text_overlay_update_wrap_mode (GstTextOverlay * overlay)
682 if (overlay->wrap_mode == GST_TEXT_OVERLAY_WRAP_MODE_NONE) {
683 GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
684 pango_layout_set_width (overlay->layout, -1);
688 if (overlay->auto_adjust_size) {
689 width = DEFAULT_SCALE_BASIS * PANGO_SCALE;
690 if (overlay->use_vertical_render) {
691 width = width * (overlay->height - overlay->ypad * 2) / overlay->width;
695 (overlay->use_vertical_render ? overlay->height : overlay->width) *
699 GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
700 GST_DEBUG_OBJECT (overlay, "Set wrap mode %d", overlay->wrap_mode);
701 pango_layout_set_width (overlay->layout, width);
702 pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
707 gst_text_overlay_update_render_mode (GstTextOverlay * overlay)
709 PangoMatrix matrix = PANGO_MATRIX_INIT;
710 PangoContext *context = pango_layout_get_context (overlay->layout);
712 if (overlay->use_vertical_render) {
713 pango_matrix_rotate (&matrix, -90);
714 pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO);
715 pango_context_set_matrix (context, &matrix);
716 pango_layout_set_alignment (overlay->layout, PANGO_ALIGN_LEFT);
718 pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
719 pango_context_set_matrix (context, &matrix);
720 pango_layout_set_alignment (overlay->layout, overlay->line_align);
725 gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps)
727 GstTextOverlay *overlay;
728 GstStructure *structure;
730 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
732 structure = gst_caps_get_structure (caps, 0);
733 overlay->have_pango_markup =
734 gst_structure_has_name (structure, "text/x-pango-markup");
736 gst_object_unref (overlay);
741 /* FIXME: upstream nego (e.g. when the video window is resized) */
744 gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps)
746 GstTextOverlay *overlay;
747 GstStructure *structure;
748 gboolean ret = FALSE;
751 if (!GST_PAD_IS_SINK (pad))
754 g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
756 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
760 structure = gst_caps_get_structure (caps, 0);
761 fps = gst_structure_get_value (structure, "framerate");
764 && gst_video_format_parse_caps (caps, &overlay->format, &overlay->width,
766 ret = gst_pad_set_caps (overlay->srcpad, caps);
769 overlay->fps_n = gst_value_get_fraction_numerator (fps);
770 overlay->fps_d = gst_value_get_fraction_denominator (fps);
773 GST_OBJECT_LOCK (overlay);
774 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
775 gst_text_overlay_update_wrap_mode (overlay);
776 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
777 GST_OBJECT_UNLOCK (overlay);
780 gst_object_unref (overlay);
786 gst_text_overlay_set_property (GObject * object, guint prop_id,
787 const GValue * value, GParamSpec * pspec)
789 GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
791 GST_OBJECT_LOCK (overlay);
794 g_free (overlay->default_text);
795 overlay->default_text = g_value_dup_string (value);
796 overlay->need_render = TRUE;
799 overlay->want_shading = g_value_get_boolean (value);
802 overlay->xpad = g_value_get_int (value);
805 overlay->ypad = g_value_get_int (value);
808 overlay->deltax = g_value_get_int (value);
811 overlay->deltay = g_value_get_int (value);
814 overlay->xpos = g_value_get_double (value);
817 overlay->ypos = g_value_get_double (value);
820 const gchar *s = g_value_get_string (value);
822 if (s && g_ascii_strcasecmp (s, "left") == 0)
823 overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
824 else if (s && g_ascii_strcasecmp (s, "center") == 0)
825 overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
826 else if (s && g_ascii_strcasecmp (s, "right") == 0)
827 overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
829 g_warning ("Invalid value '%s' for textoverlay property 'halign'",
834 const gchar *s = g_value_get_string (value);
836 if (s && g_ascii_strcasecmp (s, "baseline") == 0)
837 overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
838 else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
839 overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM;
840 else if (s && g_ascii_strcasecmp (s, "top") == 0)
841 overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
843 g_warning ("Invalid value '%s' for textoverlay property 'valign'",
847 case PROP_VALIGNMENT:
848 overlay->valign = g_value_get_enum (value);
850 case PROP_HALIGNMENT:
851 overlay->halign = g_value_get_enum (value);
854 overlay->wrap_mode = g_value_get_enum (value);
855 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
856 gst_text_overlay_update_wrap_mode (overlay);
857 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
861 PangoFontDescription *desc;
862 const gchar *fontdesc_str;
864 fontdesc_str = g_value_get_string (value);
865 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
866 desc = pango_font_description_from_string (fontdesc_str);
868 GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
869 pango_layout_set_font_description (overlay->layout, desc);
870 gst_text_overlay_adjust_values_with_fontdesc (overlay, desc);
871 pango_font_description_free (desc);
873 GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
876 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
880 overlay->color = g_value_get_uint (value);
883 overlay->silent = g_value_get_boolean (value);
885 case PROP_LINE_ALIGNMENT:
886 overlay->line_align = g_value_get_enum (value);
887 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
888 pango_layout_set_alignment (overlay->layout,
889 (PangoAlignment) overlay->line_align);
890 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
893 overlay->wait_text = g_value_get_boolean (value);
895 case PROP_AUTO_ADJUST_SIZE:
896 overlay->auto_adjust_size = g_value_get_boolean (value);
897 overlay->need_render = TRUE;
899 case PROP_VERTICAL_RENDER:
900 overlay->use_vertical_render = g_value_get_boolean (value);
901 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
902 gst_text_overlay_update_render_mode (overlay);
903 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
904 overlay->need_render = TRUE;
907 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
911 overlay->need_render = TRUE;
912 GST_OBJECT_UNLOCK (overlay);
916 gst_text_overlay_get_property (GObject * object, guint prop_id,
917 GValue * value, GParamSpec * pspec)
919 GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
921 GST_OBJECT_LOCK (overlay);
924 g_value_set_string (value, overlay->default_text);
927 g_value_set_boolean (value, overlay->want_shading);
930 g_value_set_int (value, overlay->xpad);
933 g_value_set_int (value, overlay->ypad);
936 g_value_set_int (value, overlay->deltax);
939 g_value_set_int (value, overlay->deltay);
942 g_value_set_double (value, overlay->xpos);
945 g_value_set_double (value, overlay->ypos);
947 case PROP_VALIGNMENT:
948 g_value_set_enum (value, overlay->valign);
950 case PROP_HALIGNMENT:
951 g_value_set_enum (value, overlay->halign);
954 g_value_set_enum (value, overlay->wrap_mode);
957 g_value_set_boolean (value, overlay->silent);
959 case PROP_LINE_ALIGNMENT:
960 g_value_set_enum (value, overlay->line_align);
963 g_value_set_boolean (value, overlay->wait_text);
965 case PROP_AUTO_ADJUST_SIZE:
966 g_value_set_boolean (value, overlay->auto_adjust_size);
968 case PROP_VERTICAL_RENDER:
969 g_value_set_boolean (value, overlay->use_vertical_render);
972 g_value_set_uint (value, overlay->color);
975 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
979 overlay->need_render = TRUE;
980 GST_OBJECT_UNLOCK (overlay);
984 gst_text_overlay_src_query (GstPad * pad, GstQuery * query)
986 gboolean ret = FALSE;
987 GstTextOverlay *overlay = NULL;
989 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
991 ret = gst_pad_peer_query (overlay->video_sinkpad, query);
993 gst_object_unref (overlay);
999 gst_text_overlay_src_event (GstPad * pad, GstEvent * event)
1001 gboolean ret = FALSE;
1002 GstTextOverlay *overlay = NULL;
1004 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
1006 switch (GST_EVENT_TYPE (event)) {
1007 case GST_EVENT_SEEK:{
1010 /* We don't handle seek if we have not text pad */
1011 if (!overlay->text_linked) {
1012 GST_DEBUG_OBJECT (overlay, "seek received, pushing upstream");
1013 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1017 GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
1019 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1021 /* Flush downstream, only for flushing seek */
1022 if (flags & GST_SEEK_FLAG_FLUSH)
1023 gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
1025 /* Mark ourself as flushing, unblock chains */
1026 GST_OBJECT_LOCK (overlay);
1027 overlay->video_flushing = TRUE;
1028 overlay->text_flushing = TRUE;
1029 gst_text_overlay_pop_text (overlay);
1030 GST_OBJECT_UNLOCK (overlay);
1032 /* Seek on each sink pad */
1033 gst_event_ref (event);
1034 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1036 ret = gst_pad_push_event (overlay->text_sinkpad, event);
1038 gst_event_unref (event);
1043 if (overlay->text_linked) {
1044 gst_event_ref (event);
1045 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1046 gst_pad_push_event (overlay->text_sinkpad, event);
1048 ret = gst_pad_push_event (overlay->video_sinkpad, event);
1054 gst_object_unref (overlay);
1060 gst_text_overlay_getcaps (GstPad * pad)
1062 GstTextOverlay *overlay;
1066 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
1068 if (pad == overlay->srcpad)
1069 otherpad = overlay->video_sinkpad;
1071 otherpad = overlay->srcpad;
1073 /* we can do what the peer can */
1074 caps = gst_pad_peer_get_caps (otherpad);
1077 const GstCaps *templ;
1079 GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
1081 /* filtered against our padtemplate */
1082 templ = gst_pad_get_pad_template_caps (otherpad);
1083 GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
1084 temp = gst_caps_intersect (caps, templ);
1085 GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
1086 gst_caps_unref (caps);
1087 /* this is what we can do */
1090 /* no peer, our padtemplate is enough then */
1091 caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
1094 GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
1096 gst_object_unref (overlay);
1102 gst_text_overlay_adjust_values_with_fontdesc (GstTextOverlay * overlay,
1103 PangoFontDescription * desc)
1105 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
1106 overlay->shadow_offset = (double) (font_size) / 13.0;
1107 overlay->outline_offset = (double) (font_size) / 15.0;
1108 if (overlay->outline_offset < MINIMUM_OUTLINE_OFFSET)
1109 overlay->outline_offset = MINIMUM_OUTLINE_OFFSET;
1112 #define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
1113 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
1114 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
1115 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
1119 gst_text_overlay_blit_1 (GstTextOverlay * overlay, guchar * dest, gint xpos,
1120 gint ypos, guchar * text_image, guint dest_stride)
1127 gint width = overlay->image_width;
1128 gint height = overlay->image_height;
1134 if (xpos + width > overlay->width) {
1135 width = overlay->width - xpos;
1138 if (ypos + height > overlay->height) {
1139 height = overlay->height - ypos;
1142 dest += (ypos / 1) * dest_stride;
1144 for (i = 0; i < height; i++) {
1145 pimage = text_image + 4 * (i * overlay->image_width);
1146 py = dest + i * dest_stride + xpos;
1147 for (j = 0; j < width; j++) {
1148 b = pimage[CAIRO_ARGB_B];
1149 g = pimage[CAIRO_ARGB_G];
1150 r = pimage[CAIRO_ARGB_R];
1151 a = pimage[CAIRO_ARGB_A];
1152 CAIRO_UNPREMULTIPLY (a, r, g, b);
1159 COMP_Y (y, r, g, b);
1161 BLEND (*py++, a, y, x);
1167 gst_text_overlay_blit_sub2x2cbcr (GstTextOverlay * overlay,
1168 guchar * destcb, guchar * destcr, gint xpos, gint ypos, guchar * text_image,
1169 guint destcb_stride, guint destcr_stride, guint pix_stride)
1174 gushort r1, g1, b1, a1;
1175 guchar *pimage1, *pimage2;
1177 gint width = overlay->image_width - 2;
1178 gint height = overlay->image_height - 2;
1186 if (xpos + width > overlay->width) {
1187 width = overlay->width - xpos;
1190 if (ypos + height > overlay->height) {
1191 height = overlay->height - ypos;
1194 destcb += (ypos / 2) * destcb_stride;
1195 destcr += (ypos / 2) * destcr_stride;
1197 for (i = 0; i < height; i += 2) {
1198 pimage1 = text_image + 4 * (i * overlay->image_width);
1199 pimage2 = pimage1 + 4 * overlay->image_width;
1200 pcb = destcb + (i / 2) * destcb_stride + xpos / 2;
1201 pcr = destcr + (i / 2) * destcr_stride + xpos / 2;
1202 for (j = 0; j < width; j += 2) {
1203 b = pimage1[CAIRO_ARGB_B];
1204 g = pimage1[CAIRO_ARGB_G];
1205 r = pimage1[CAIRO_ARGB_R];
1206 a = pimage1[CAIRO_ARGB_A];
1207 CAIRO_UNPREMULTIPLY (a, r, g, b);
1210 b1 = pimage1[CAIRO_ARGB_B];
1211 g1 = pimage1[CAIRO_ARGB_G];
1212 r1 = pimage1[CAIRO_ARGB_R];
1213 a1 = pimage1[CAIRO_ARGB_A];
1214 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1221 b1 = pimage2[CAIRO_ARGB_B];
1222 g1 = pimage2[CAIRO_ARGB_G];
1223 r1 = pimage2[CAIRO_ARGB_R];
1224 a1 = pimage2[CAIRO_ARGB_A];
1225 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1232 /* + 2 for rounding */
1233 b1 = pimage2[CAIRO_ARGB_B];
1234 g1 = pimage2[CAIRO_ARGB_G];
1235 r1 = pimage2[CAIRO_ARGB_R];
1236 a1 = pimage2[CAIRO_ARGB_A];
1237 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1254 COMP_U (cb, r, g, b);
1255 COMP_V (cr, r, g, b);
1258 BLEND (*pcb, a, cb, x);
1260 BLEND (*pcr, a, cr, x);
1269 gst_text_overlay_render_pangocairo (GstTextOverlay * overlay,
1270 const gchar * string, gint textlen)
1273 cairo_surface_t *surface;
1274 PangoRectangle ink_rect, logical_rect;
1275 cairo_matrix_t cairo_matrix;
1277 double scalef = 1.0;
1280 g_mutex_lock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1282 if (overlay->auto_adjust_size) {
1283 /* 640 pixel is default */
1284 scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS;
1286 pango_layout_set_width (overlay->layout, -1);
1287 /* set text on pango layout */
1288 pango_layout_set_markup (overlay->layout, string, textlen);
1290 /* get subtitle image size */
1291 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1293 width = (logical_rect.width + overlay->shadow_offset) * scalef;
1295 if (width + overlay->deltax >
1296 (overlay->use_vertical_render ? overlay->height : overlay->width)) {
1298 * subtitle image width is larger then overlay width
1299 * so rearrange overlay wrap mode.
1301 gst_text_overlay_update_wrap_mode (overlay);
1302 pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
1303 width = overlay->width;
1307 (logical_rect.height + logical_rect.y + overlay->shadow_offset) * scalef;
1308 if (height > overlay->height) {
1309 height = overlay->height;
1311 if (overlay->use_vertical_render) {
1312 PangoRectangle rect;
1313 PangoContext *context;
1314 PangoMatrix matrix = PANGO_MATRIX_INIT;
1317 context = pango_layout_get_context (overlay->layout);
1319 pango_matrix_rotate (&matrix, -90);
1321 rect.x = rect.y = 0;
1323 rect.height = height;
1324 pango_matrix_transform_pixel_rectangle (&matrix, &rect);
1325 matrix.x0 = -rect.x;
1326 matrix.y0 = -rect.y;
1328 pango_context_set_matrix (context, &matrix);
1330 cairo_matrix.xx = matrix.xx;
1331 cairo_matrix.yx = matrix.yx;
1332 cairo_matrix.xy = matrix.xy;
1333 cairo_matrix.yy = matrix.yy;
1334 cairo_matrix.x0 = matrix.x0;
1335 cairo_matrix.y0 = matrix.y0;
1336 cairo_matrix_scale (&cairo_matrix, scalef, scalef);
1342 cairo_matrix_init_scale (&cairo_matrix, scalef, scalef);
1345 /* reallocate surface */
1346 overlay->text_image = g_realloc (overlay->text_image, 4 * width * height);
1348 surface = cairo_image_surface_create_for_data (overlay->text_image,
1349 CAIRO_FORMAT_ARGB32, width, height, width * 4);
1350 cr = cairo_create (surface);
1353 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1356 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1358 if (overlay->want_shading)
1359 cairo_paint_with_alpha (cr, overlay->shading_value);
1361 /* apply transformations */
1362 cairo_set_matrix (cr, &cairo_matrix);
1364 /* FIXME: We use show_layout everywhere except for the surface
1365 * because it's really faster and internally does all kinds of
1366 * caching. Unfortunately we have to paint to a cairo path for
1367 * the outline and this is slow. Once Pango supports user fonts
1368 * we should use them, see
1369 * https://bugzilla.gnome.org/show_bug.cgi?id=598695
1371 * Idea would the be, to create a cairo user font that
1372 * does shadow, outline, text painting in the
1373 * render_glyph function.
1376 /* draw shadow text */
1378 cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
1379 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1380 pango_cairo_show_layout (cr, overlay->layout);
1383 /* draw outline text */
1385 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1386 cairo_set_line_width (cr, overlay->outline_offset);
1387 pango_cairo_layout_path (cr, overlay->layout);
1391 a = (overlay->color >> 24) & 0xff;
1392 r = (overlay->color >> 16) & 0xff;
1393 g = (overlay->color >> 8) & 0xff;
1394 b = (overlay->color >> 0) & 0xff;
1398 cairo_set_source_rgba (cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
1399 pango_cairo_show_layout (cr, overlay->layout);
1403 cairo_surface_destroy (surface);
1404 overlay->image_width = width;
1405 overlay->image_height = height;
1406 overlay->baseline_y = ink_rect.y;
1408 g_mutex_unlock (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock);
1415 gst_text_overlay_shade_planar_Y (GstTextOverlay * overlay, guchar * dest,
1416 gint x0, gint x1, gint y0, gint y1)
1418 gint i, j, dest_stride;
1420 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1423 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1424 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1426 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1427 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1429 for (i = y0; i < y1; ++i) {
1430 for (j = x0; j < x1; ++j) {
1431 gint y = dest[(i * dest_stride) + j] + overlay->shading_value;
1433 dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
1439 gst_text_overlay_shade_packed_Y (GstTextOverlay * overlay, guchar * dest,
1440 gint x0, gint x1, gint y0, gint y1)
1443 guint dest_stride, pixel_stride, component_offset;
1445 dest_stride = gst_video_format_get_row_stride (overlay->format, 0,
1447 pixel_stride = gst_video_format_get_pixel_stride (overlay->format, 0);
1449 gst_video_format_get_component_offset (overlay->format, 0, overlay->width,
1452 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
1453 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
1455 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
1456 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
1459 x0 = gst_video_format_get_component_width (overlay->format, 0, x0);
1461 x1 = gst_video_format_get_component_width (overlay->format, 0, x1);
1464 y0 = gst_video_format_get_component_height (overlay->format, 0, y0);
1466 y1 = gst_video_format_get_component_height (overlay->format, 0, y1);
1468 for (i = y0; i < y1; i++) {
1469 for (j = x0; j < x1; j++) {
1473 y_pos = (i * dest_stride) + j * pixel_stride + component_offset;
1474 y = dest[y_pos] + overlay->shading_value;
1476 dest[y_pos] = CLAMP (y, 0, 255);
1481 #define gst_text_overlay_shade_BGRx gst_text_overlay_shade_xRGB
1482 #define gst_text_overlay_shade_RGBx gst_text_overlay_shade_xRGB
1483 #define gst_text_overlay_shade_xBGR gst_text_overlay_shade_xRGB
1485 gst_text_overlay_shade_xRGB (GstTextOverlay * overlay, guchar * dest,
1486 gint x0, gint x1, gint y0, gint y1)
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);
1496 for (i = y0; i < y1; i++) {
1497 for (j = x0; j < x1; j++) {
1500 y_pos = (i * 4 * overlay->width) + j * 4;
1501 for (k = 0; k < 4; k++) {
1502 y = dest[y_pos + k] + overlay->shading_value;
1503 dest[y_pos + k] = CLAMP (y, 0, 255);
1509 #define ARGB_SHADE_FUNCTION(name, OFFSET) \
1510 static inline void \
1511 gst_text_overlay_shade_##name (GstTextOverlay * overlay, guchar * dest, \
1512 gint x0, gint x1, gint y0, gint y1) \
1516 x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
1517 x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
1519 y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
1520 y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
1522 for (i = y0; i < y1; i++) {\
1523 for (j = x0; j < x1; j++) {\
1525 y_pos = (i * 4 * overlay->width) + j * 4;\
1526 for (k = OFFSET; k < 3+OFFSET; k++) {\
1527 y = dest[y_pos + k] + overlay->shading_value;\
1528 dest[y_pos + k] = CLAMP (y, 0, 255);\
1533 ARGB_SHADE_FUNCTION (ARGB, 1);
1534 ARGB_SHADE_FUNCTION (ABGR, 1);
1535 ARGB_SHADE_FUNCTION (RGBA, 0);
1536 ARGB_SHADE_FUNCTION (BGRA, 0);
1540 * - use proper strides and offset for I420
1541 * - don't draw over the edge of the picture (try a longer
1542 * text with a huge font size)
1546 gst_text_overlay_blit_NV12_NV21 (GstTextOverlay * overlay,
1547 guint8 * yuv_pixels, gint xpos, gint ypos)
1549 int y_stride, uv_stride;
1550 int u_offset, v_offset;
1553 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1554 * to a boundary of integer number of U/V pixels:
1556 xpos = GST_ROUND_UP_2 (xpos);
1557 ypos = GST_ROUND_UP_2 (ypos);
1560 h = overlay->height;
1562 y_stride = gst_video_format_get_row_stride (overlay->format, 0, w);
1563 uv_stride = gst_video_format_get_row_stride (overlay->format, 1, w);
1564 u_offset = gst_video_format_get_component_offset (overlay->format, 1, w, h);
1565 v_offset = gst_video_format_get_component_offset (overlay->format, 2, w, h);
1567 gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image,
1569 gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1570 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, uv_stride,
1575 gst_text_overlay_blit_I420 (GstTextOverlay * overlay,
1576 guint8 * yuv_pixels, gint xpos, gint ypos)
1578 int y_stride, u_stride, v_stride;
1579 int u_offset, v_offset;
1582 /* because U/V is 2x2 subsampled, we need to round, either up or down,
1583 * to a boundary of integer number of U/V pixels:
1585 xpos = GST_ROUND_UP_2 (xpos);
1586 ypos = GST_ROUND_UP_2 (ypos);
1589 h = overlay->height;
1591 y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, w);
1592 u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, w);
1593 v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, w);
1595 gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1, w, h);
1597 gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, w, h);
1599 gst_text_overlay_blit_1 (overlay, yuv_pixels, xpos, ypos, overlay->text_image,
1601 gst_text_overlay_blit_sub2x2cbcr (overlay, yuv_pixels + u_offset,
1602 yuv_pixels + v_offset, xpos, ypos, overlay->text_image, u_stride,
1607 gst_text_overlay_blit_UYVY (GstTextOverlay * overlay,
1608 guint8 * yuv_pixels, gint xpos, gint ypos)
1615 guchar *pimage, *dest;
1617 /* because U/V is 2x horizontally subsampled, we need to round to a
1618 * boundary of integer number of U/V pixels in x dimension:
1620 xpos = GST_ROUND_UP_2 (xpos);
1622 w = overlay->image_width - 2;
1623 h = overlay->image_height - 2;
1629 if (xpos + w > overlay->width) {
1630 w = overlay->width - xpos;
1633 if (ypos + h > overlay->height) {
1634 h = overlay->height - ypos;
1637 for (i = 0; i < h; i++) {
1638 pimage = overlay->text_image + i * overlay->image_width * 4;
1639 dest = yuv_pixels + (i + ypos) * overlay->width * 2 + xpos * 2;
1640 for (j = 0; j < w; j += 2) {
1641 b0 = pimage[CAIRO_ARGB_B];
1642 g0 = pimage[CAIRO_ARGB_G];
1643 r0 = pimage[CAIRO_ARGB_R];
1644 a0 = pimage[CAIRO_ARGB_A];
1645 CAIRO_UNPREMULTIPLY (a0, r0, g0, b0);
1648 b1 = pimage[CAIRO_ARGB_B];
1649 g1 = pimage[CAIRO_ARGB_G];
1650 r1 = pimage[CAIRO_ARGB_R];
1651 a1 = pimage[CAIRO_ARGB_A];
1652 CAIRO_UNPREMULTIPLY (a1, r1, g1, b1);
1662 COMP_Y (y0, r0, g0, b0);
1663 COMP_Y (y1, r1, g1, b1);
1673 COMP_U (u, r0, g0, b0);
1674 COMP_V (v, r0, g0, b0);
1676 BLEND (*dest, a0, u, *dest);
1678 BLEND (*dest, a0, y0, *dest);
1680 BLEND (*dest, a0, v, *dest);
1682 BLEND (*dest, a0, y1, *dest);
1689 gst_text_overlay_blit_AYUV (GstTextOverlay * overlay,
1690 guint8 * rgb_pixels, gint xpos, gint ypos)
1696 guchar *pimage, *dest;
1698 w = overlay->image_width;
1699 h = overlay->image_height;
1705 if (xpos + w > overlay->width) {
1706 w = overlay->width - xpos;
1709 if (ypos + h > overlay->height) {
1710 h = overlay->height - ypos;
1713 for (i = 0; i < h; i++) {
1714 pimage = overlay->text_image + i * overlay->image_width * 4;
1715 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4;
1716 for (j = 0; j < w; j++) {
1717 a = pimage[CAIRO_ARGB_A];
1718 b = pimage[CAIRO_ARGB_B];
1719 g = pimage[CAIRO_ARGB_G];
1720 r = pimage[CAIRO_ARGB_R];
1722 CAIRO_UNPREMULTIPLY (a, r, g, b);
1724 // convert background to yuv
1725 COMP_Y (y, r, g, b);
1726 COMP_U (u, r, g, b);
1727 COMP_V (v, r, g, b);
1729 // preform text "OVER" background alpha compositing
1730 a1 = a + (dest[0] * (255 - a)) / 255 + 1; // add 1 to prevent divide by 0
1731 OVER (dest[1], a, y, dest[0], dest[1], a1);
1732 OVER (dest[2], a, u, dest[0], dest[2], a1);
1733 OVER (dest[3], a, v, dest[0], dest[3], a1);
1734 dest[0] = a1 - 1; // remove the temporary 1 we added
1742 #define xRGB_BLIT_FUNCTION(name, R, G, B) \
1743 static inline void \
1744 gst_text_overlay_blit_##name (GstTextOverlay * overlay, \
1745 guint8 * rgb_pixels, gint xpos, gint ypos) \
1750 guchar *pimage, *dest; \
1752 w = overlay->image_width; \
1753 h = overlay->image_height; \
1759 if (xpos + w > overlay->width) { \
1760 w = overlay->width - xpos; \
1763 if (ypos + h > overlay->height) { \
1764 h = overlay->height - ypos; \
1767 for (i = 0; i < h; i++) { \
1768 pimage = overlay->text_image + i * overlay->image_width * 4; \
1769 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1770 for (j = 0; j < w; j++) { \
1771 a = pimage[CAIRO_ARGB_A]; \
1772 b = pimage[CAIRO_ARGB_B]; \
1773 g = pimage[CAIRO_ARGB_G]; \
1774 r = pimage[CAIRO_ARGB_R]; \
1775 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1776 b = (b*a + dest[B] * (255-a)) / 255; \
1777 g = (g*a + dest[G] * (255-a)) / 255; \
1778 r = (r*a + dest[R] * (255-a)) / 255; \
1788 xRGB_BLIT_FUNCTION (xRGB, 1, 2, 3);
1789 xRGB_BLIT_FUNCTION (BGRx, 2, 1, 0);
1790 xRGB_BLIT_FUNCTION (xBGR, 3, 2, 1);
1791 xRGB_BLIT_FUNCTION (RGBx, 0, 1, 2);
1793 #define ARGB_BLIT_FUNCTION(name, A, R, G, B) \
1794 static inline void \
1795 gst_text_overlay_blit_##name (GstTextOverlay * overlay, \
1796 guint8 * rgb_pixels, gint xpos, gint ypos) \
1798 int a, r, g, b, a1; \
1801 guchar *pimage, *dest; \
1803 w = overlay->image_width; \
1804 h = overlay->image_height; \
1810 if (xpos + w > overlay->width) { \
1811 w = overlay->width - xpos; \
1814 if (ypos + h > overlay->height) { \
1815 h = overlay->height - ypos; \
1818 for (i = 0; i < h; i++) { \
1819 pimage = overlay->text_image + i * overlay->image_width * 4; \
1820 dest = rgb_pixels + (i + ypos) * 4 * overlay->width + xpos * 4; \
1821 for (j = 0; j < w; j++) { \
1822 a = pimage[CAIRO_ARGB_A]; \
1823 b = pimage[CAIRO_ARGB_B]; \
1824 g = pimage[CAIRO_ARGB_G]; \
1825 r = pimage[CAIRO_ARGB_R]; \
1826 CAIRO_UNPREMULTIPLY (a, r, g, b); \
1827 a1 = a + (dest[A] * (255 - a)) / 255 + 1; \
1828 OVER (dest[R], a, r, dest[0], dest[R], a1); \
1829 OVER (dest[G], a, g, dest[0], dest[G], a1); \
1830 OVER (dest[B], a, b, dest[0], dest[B], a1); \
1837 ARGB_BLIT_FUNCTION (RGBA, 3, 0, 1, 2);
1838 ARGB_BLIT_FUNCTION (BGRA, 3, 2, 1, 0);
1839 ARGB_BLIT_FUNCTION (ARGB, 0, 1, 2, 3);
1840 ARGB_BLIT_FUNCTION (ABGR, 0, 3, 2, 1);
1843 gst_text_overlay_render_text (GstTextOverlay * overlay,
1844 const gchar * text, gint textlen)
1848 if (!overlay->need_render) {
1849 GST_DEBUG ("Using previously rendered text.");
1853 /* -1 is the whole string */
1854 if (text != NULL && textlen < 0) {
1855 textlen = strlen (text);
1859 string = g_strndup (text, textlen);
1860 } else { /* empty string */
1861 string = g_strdup (" ");
1863 g_strdelimit (string, "\r\t", ' ');
1864 textlen = strlen (string);
1866 /* FIXME: should we check for UTF-8 here? */
1868 GST_DEBUG ("Rendering '%s'", string);
1869 gst_text_overlay_render_pangocairo (overlay, string, textlen);
1873 overlay->need_render = FALSE;
1876 static GstFlowReturn
1877 gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
1881 GstTextOverlayVAlign valign;
1882 GstTextOverlayHAlign halign;
1884 width = overlay->image_width;
1885 height = overlay->image_height;
1887 video_frame = gst_buffer_make_writable (video_frame);
1889 if (overlay->use_vertical_render)
1890 halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
1892 halign = overlay->halign;
1895 case GST_TEXT_OVERLAY_HALIGN_LEFT:
1896 xpos = overlay->xpad;
1898 case GST_TEXT_OVERLAY_HALIGN_CENTER:
1899 xpos = (overlay->width - width) / 2;
1901 case GST_TEXT_OVERLAY_HALIGN_RIGHT:
1902 xpos = overlay->width - width - overlay->xpad;
1904 case GST_TEXT_OVERLAY_HALIGN_POS:
1905 xpos = (gint) (overlay->width * overlay->xpos) - width / 2;
1906 xpos = CLAMP (xpos, 0, overlay->width - width);
1913 xpos += overlay->deltax;
1915 if (overlay->use_vertical_render)
1916 valign = GST_TEXT_OVERLAY_VALIGN_TOP;
1918 valign = overlay->valign;
1921 case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
1922 ypos = overlay->height - height - overlay->ypad;
1924 case GST_TEXT_OVERLAY_VALIGN_BASELINE:
1925 ypos = overlay->height - (height + overlay->ypad);
1927 case GST_TEXT_OVERLAY_VALIGN_TOP:
1928 ypos = overlay->ypad;
1930 case GST_TEXT_OVERLAY_VALIGN_POS:
1931 ypos = (gint) (overlay->height * overlay->ypos) - height / 2;
1932 ypos = CLAMP (ypos, 0, overlay->height - height);
1934 case GST_TEXT_OVERLAY_VALIGN_CENTER:
1935 ypos = (overlay->height - height) / 2;
1938 ypos = overlay->ypad;
1941 ypos += overlay->deltay;
1943 /* shaded background box */
1944 if (overlay->want_shading) {
1945 switch (overlay->format) {
1946 case GST_VIDEO_FORMAT_I420:
1947 case GST_VIDEO_FORMAT_NV12:
1948 case GST_VIDEO_FORMAT_NV21:
1949 gst_text_overlay_shade_planar_Y (overlay,
1950 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1951 ypos, ypos + overlay->image_height);
1953 case GST_VIDEO_FORMAT_AYUV:
1954 case GST_VIDEO_FORMAT_UYVY:
1955 gst_text_overlay_shade_packed_Y (overlay,
1956 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1957 ypos, ypos + overlay->image_height);
1959 case GST_VIDEO_FORMAT_xRGB:
1960 gst_text_overlay_shade_xRGB (overlay,
1961 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1962 ypos, ypos + overlay->image_height);
1964 case GST_VIDEO_FORMAT_xBGR:
1965 gst_text_overlay_shade_xBGR (overlay,
1966 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1967 ypos, ypos + overlay->image_height);
1969 case GST_VIDEO_FORMAT_BGRx:
1970 gst_text_overlay_shade_BGRx (overlay,
1971 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1972 ypos, ypos + overlay->image_height);
1974 case GST_VIDEO_FORMAT_RGBx:
1975 gst_text_overlay_shade_RGBx (overlay,
1976 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1977 ypos, ypos + overlay->image_height);
1979 case GST_VIDEO_FORMAT_ARGB:
1980 gst_text_overlay_shade_ARGB (overlay,
1981 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1982 ypos, ypos + overlay->image_height);
1984 case GST_VIDEO_FORMAT_ABGR:
1985 gst_text_overlay_shade_ABGR (overlay,
1986 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1987 ypos, ypos + overlay->image_height);
1989 case GST_VIDEO_FORMAT_RGBA:
1990 gst_text_overlay_shade_RGBA (overlay,
1991 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1992 ypos, ypos + overlay->image_height);
1994 case GST_VIDEO_FORMAT_BGRA:
1995 gst_text_overlay_shade_BGRA (overlay,
1996 GST_BUFFER_DATA (video_frame), xpos, xpos + overlay->image_width,
1997 ypos, ypos + overlay->image_height);
2000 g_assert_not_reached ();
2007 if (overlay->text_image) {
2008 switch (overlay->format) {
2009 case GST_VIDEO_FORMAT_I420:
2010 gst_text_overlay_blit_I420 (overlay,
2011 GST_BUFFER_DATA (video_frame), xpos, ypos);
2013 case GST_VIDEO_FORMAT_NV12:
2014 case GST_VIDEO_FORMAT_NV21:
2015 gst_text_overlay_blit_NV12_NV21 (overlay,
2016 GST_BUFFER_DATA (video_frame), xpos, ypos);
2018 case GST_VIDEO_FORMAT_UYVY:
2019 gst_text_overlay_blit_UYVY (overlay,
2020 GST_BUFFER_DATA (video_frame), xpos, ypos);
2022 case GST_VIDEO_FORMAT_AYUV:
2023 gst_text_overlay_blit_AYUV (overlay,
2024 GST_BUFFER_DATA (video_frame), xpos, ypos);
2026 case GST_VIDEO_FORMAT_BGRx:
2027 gst_text_overlay_blit_BGRx (overlay,
2028 GST_BUFFER_DATA (video_frame), xpos, ypos);
2030 case GST_VIDEO_FORMAT_xRGB:
2031 gst_text_overlay_blit_xRGB (overlay,
2032 GST_BUFFER_DATA (video_frame), xpos, ypos);
2034 case GST_VIDEO_FORMAT_RGBx:
2035 gst_text_overlay_blit_RGBx (overlay,
2036 GST_BUFFER_DATA (video_frame), xpos, ypos);
2038 case GST_VIDEO_FORMAT_xBGR:
2039 gst_text_overlay_blit_xBGR (overlay,
2040 GST_BUFFER_DATA (video_frame), xpos, ypos);
2042 case GST_VIDEO_FORMAT_ARGB:
2043 gst_text_overlay_blit_ARGB (overlay,
2044 GST_BUFFER_DATA (video_frame), xpos, ypos);
2046 case GST_VIDEO_FORMAT_ABGR:
2047 gst_text_overlay_blit_ABGR (overlay,
2048 GST_BUFFER_DATA (video_frame), xpos, ypos);
2050 case GST_VIDEO_FORMAT_RGBA:
2051 gst_text_overlay_blit_RGBA (overlay,
2052 GST_BUFFER_DATA (video_frame), xpos, ypos);
2054 case GST_VIDEO_FORMAT_BGRA:
2055 gst_text_overlay_blit_BGRA (overlay,
2056 GST_BUFFER_DATA (video_frame), xpos, ypos);
2059 g_assert_not_reached ();
2062 return gst_pad_push (overlay->srcpad, video_frame);
2065 static GstPadLinkReturn
2066 gst_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
2068 GstTextOverlay *overlay;
2070 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
2072 GST_DEBUG_OBJECT (overlay, "Text pad linked");
2074 overlay->text_linked = TRUE;
2076 gst_object_unref (overlay);
2078 return GST_PAD_LINK_OK;
2082 gst_text_overlay_text_pad_unlink (GstPad * pad)
2084 GstTextOverlay *overlay;
2086 /* don't use gst_pad_get_parent() here, will deadlock */
2087 overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2089 GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
2091 overlay->text_linked = FALSE;
2093 gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
2097 gst_text_overlay_text_event (GstPad * pad, GstEvent * event)
2099 gboolean ret = FALSE;
2100 GstTextOverlay *overlay = NULL;
2102 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
2104 GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2106 switch (GST_EVENT_TYPE (event)) {
2107 case GST_EVENT_NEWSEGMENT:{
2110 gdouble rate, applied_rate;
2111 gint64 cur, stop, time;
2113 overlay->text_eos = FALSE;
2115 gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
2116 &fmt, &cur, &stop, &time);
2118 if (fmt == GST_FORMAT_TIME) {
2119 GST_OBJECT_LOCK (overlay);
2120 gst_segment_set_newsegment_full (&overlay->text_segment, update, rate,
2121 applied_rate, GST_FORMAT_TIME, cur, stop, time);
2122 GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
2123 &overlay->text_segment);
2124 GST_OBJECT_UNLOCK (overlay);
2126 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2127 ("received non-TIME newsegment event on text input"));
2130 gst_event_unref (event);
2133 /* wake up the video chain, it might be waiting for a text buffer or
2134 * a text segment update */
2135 GST_OBJECT_LOCK (overlay);
2136 GST_TEXT_OVERLAY_BROADCAST (overlay);
2137 GST_OBJECT_UNLOCK (overlay);
2140 case GST_EVENT_FLUSH_STOP:
2141 GST_OBJECT_LOCK (overlay);
2142 GST_INFO_OBJECT (overlay, "text flush stop");
2143 overlay->text_flushing = FALSE;
2144 overlay->text_eos = FALSE;
2145 gst_text_overlay_pop_text (overlay);
2146 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2147 GST_OBJECT_UNLOCK (overlay);
2148 gst_event_unref (event);
2151 case GST_EVENT_FLUSH_START:
2152 GST_OBJECT_LOCK (overlay);
2153 GST_INFO_OBJECT (overlay, "text flush start");
2154 overlay->text_flushing = TRUE;
2155 GST_TEXT_OVERLAY_BROADCAST (overlay);
2156 GST_OBJECT_UNLOCK (overlay);
2157 gst_event_unref (event);
2161 GST_OBJECT_LOCK (overlay);
2162 overlay->text_eos = TRUE;
2163 GST_INFO_OBJECT (overlay, "text EOS");
2164 /* wake up the video chain, it might be waiting for a text buffer or
2165 * a text segment update */
2166 GST_TEXT_OVERLAY_BROADCAST (overlay);
2167 GST_OBJECT_UNLOCK (overlay);
2168 gst_event_unref (event);
2172 ret = gst_pad_event_default (pad, event);
2176 gst_object_unref (overlay);
2182 gst_text_overlay_video_event (GstPad * pad, GstEvent * event)
2184 gboolean ret = FALSE;
2185 GstTextOverlay *overlay = NULL;
2187 overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
2189 GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
2191 switch (GST_EVENT_TYPE (event)) {
2192 case GST_EVENT_NEWSEGMENT:
2196 gint64 start, stop, time;
2199 GST_DEBUG_OBJECT (overlay, "received new segment");
2201 gst_event_parse_new_segment (event, &update, &rate, &format, &start,
2204 if (format == GST_FORMAT_TIME) {
2205 GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
2208 gst_segment_set_newsegment (&overlay->segment, update, rate, format,
2211 GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
2212 ("received non-TIME newsegment event on video input"));
2215 ret = gst_pad_event_default (pad, event);
2219 GST_OBJECT_LOCK (overlay);
2220 GST_INFO_OBJECT (overlay, "video EOS");
2221 overlay->video_eos = TRUE;
2222 GST_OBJECT_UNLOCK (overlay);
2223 ret = gst_pad_event_default (pad, event);
2225 case GST_EVENT_FLUSH_START:
2226 GST_OBJECT_LOCK (overlay);
2227 GST_INFO_OBJECT (overlay, "video flush start");
2228 overlay->video_flushing = TRUE;
2229 GST_TEXT_OVERLAY_BROADCAST (overlay);
2230 GST_OBJECT_UNLOCK (overlay);
2231 ret = gst_pad_event_default (pad, event);
2233 case GST_EVENT_FLUSH_STOP:
2234 GST_OBJECT_LOCK (overlay);
2235 GST_INFO_OBJECT (overlay, "video flush stop");
2236 overlay->video_flushing = FALSE;
2237 overlay->video_eos = FALSE;
2238 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2239 GST_OBJECT_UNLOCK (overlay);
2240 ret = gst_pad_event_default (pad, event);
2243 ret = gst_pad_event_default (pad, event);
2247 gst_object_unref (overlay);
2252 static GstFlowReturn
2253 gst_text_overlay_video_bufferalloc (GstPad * pad, guint64 offset, guint size,
2254 GstCaps * caps, GstBuffer ** buffer)
2256 GstTextOverlay *overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
2257 GstFlowReturn ret = GST_FLOW_WRONG_STATE;
2260 GST_OBJECT_LOCK (overlay);
2261 allocpad = overlay->srcpad ? gst_object_ref (overlay->srcpad) : NULL;
2262 GST_OBJECT_UNLOCK (overlay);
2265 ret = gst_pad_alloc_buffer (allocpad, offset, size, caps, buffer);
2266 gst_object_unref (allocpad);
2269 gst_object_unref (overlay);
2273 /* Called with lock held */
2275 gst_text_overlay_pop_text (GstTextOverlay * overlay)
2277 g_return_if_fail (GST_IS_TEXT_OVERLAY (overlay));
2279 if (overlay->text_buffer) {
2280 GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
2281 overlay->text_buffer);
2282 gst_buffer_unref (overlay->text_buffer);
2283 overlay->text_buffer = NULL;
2286 /* Let the text task know we used that buffer */
2287 GST_TEXT_OVERLAY_BROADCAST (overlay);
2290 /* We receive text buffers here. If they are out of segment we just ignore them.
2291 If the buffer is in our segment we keep it internally except if another one
2292 is already waiting here, in that case we wait that it gets kicked out */
2293 static GstFlowReturn
2294 gst_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
2296 GstFlowReturn ret = GST_FLOW_OK;
2297 GstTextOverlay *overlay = NULL;
2298 gboolean in_seg = FALSE;
2299 gint64 clip_start = 0, clip_stop = 0;
2301 overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2303 GST_OBJECT_LOCK (overlay);
2305 if (overlay->text_flushing) {
2306 GST_OBJECT_UNLOCK (overlay);
2307 ret = GST_FLOW_WRONG_STATE;
2308 GST_LOG_OBJECT (overlay, "text flushing");
2312 if (overlay->text_eos) {
2313 GST_OBJECT_UNLOCK (overlay);
2314 ret = GST_FLOW_UNEXPECTED;
2315 GST_LOG_OBJECT (overlay, "text EOS");
2319 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2320 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2321 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
2322 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
2323 GST_BUFFER_DURATION (buffer)));
2325 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))) {
2328 if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buffer)))
2329 stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
2331 stop = GST_CLOCK_TIME_NONE;
2333 in_seg = gst_segment_clip (&overlay->text_segment, GST_FORMAT_TIME,
2334 GST_BUFFER_TIMESTAMP (buffer), stop, &clip_start, &clip_stop);
2340 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2341 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2342 else if (GST_BUFFER_DURATION_IS_VALID (buffer))
2343 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2345 /* Wait for the previous buffer to go away */
2346 while (overlay->text_buffer != NULL) {
2347 GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
2348 GST_DEBUG_PAD_NAME (pad));
2349 GST_TEXT_OVERLAY_WAIT (overlay);
2350 GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
2351 if (overlay->text_flushing) {
2352 GST_OBJECT_UNLOCK (overlay);
2353 ret = GST_FLOW_WRONG_STATE;
2358 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2359 gst_segment_set_last_stop (&overlay->text_segment, GST_FORMAT_TIME,
2362 overlay->text_buffer = buffer;
2363 /* That's a new text buffer we need to render */
2364 overlay->need_render = TRUE;
2366 /* in case the video chain is waiting for a text buffer, wake it up */
2367 GST_TEXT_OVERLAY_BROADCAST (overlay);
2370 GST_OBJECT_UNLOCK (overlay);
2377 static GstFlowReturn
2378 gst_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
2380 GstTextOverlayClass *klass;
2381 GstTextOverlay *overlay;
2382 GstFlowReturn ret = GST_FLOW_OK;
2383 gboolean in_seg = FALSE;
2384 gint64 start, stop, clip_start = 0, clip_stop = 0;
2387 overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
2388 klass = GST_TEXT_OVERLAY_GET_CLASS (overlay);
2390 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
2391 goto missing_timestamp;
2393 /* ignore buffers that are outside of the current segment */
2394 start = GST_BUFFER_TIMESTAMP (buffer);
2396 if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
2397 stop = GST_CLOCK_TIME_NONE;
2399 stop = start + GST_BUFFER_DURATION (buffer);
2402 GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT " BUFFER: ts=%"
2403 GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, &overlay->segment,
2404 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2406 /* segment_clip() will adjust start unconditionally to segment_start if
2407 * no stop time is provided, so handle this ourselves */
2408 if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment.start)
2409 goto out_of_segment;
2411 in_seg = gst_segment_clip (&overlay->segment, GST_FORMAT_TIME, start, stop,
2412 &clip_start, &clip_stop);
2415 goto out_of_segment;
2417 /* if the buffer is only partially in the segment, fix up stamps */
2418 if (clip_start != start || (stop != -1 && clip_stop != stop)) {
2419 GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
2420 buffer = gst_buffer_make_metadata_writable (buffer);
2421 GST_BUFFER_TIMESTAMP (buffer) = clip_start;
2423 GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
2426 /* now, after we've done the clipping, fix up end time if there's no
2427 * duration (we only use those estimated values internally though, we
2428 * don't want to set bogus values on the buffer itself) */
2431 gint fps_num, fps_denom;
2433 s = gst_caps_get_structure (GST_PAD_CAPS (pad), 0);
2434 if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom) &&
2435 fps_num && fps_denom) {
2436 GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
2437 stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
2439 GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
2440 stop = start + 1; /* we need to assume some interval */
2444 gst_object_sync_values (G_OBJECT (overlay), GST_BUFFER_TIMESTAMP (buffer));
2448 GST_OBJECT_LOCK (overlay);
2450 if (overlay->video_flushing)
2453 if (overlay->video_eos)
2456 if (overlay->silent) {
2457 GST_OBJECT_UNLOCK (overlay);
2458 ret = gst_pad_push (overlay->srcpad, buffer);
2460 /* Update last_stop */
2461 gst_segment_set_last_stop (&overlay->segment, GST_FORMAT_TIME, clip_start);
2466 /* Text pad not linked, rendering internal text */
2467 if (!overlay->text_linked) {
2468 if (klass->get_text) {
2469 text = klass->get_text (overlay, buffer);
2471 text = g_strdup (overlay->default_text);
2474 GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
2475 "text: '%s'", GST_STR_NULL (text));
2477 GST_OBJECT_UNLOCK (overlay);
2479 if (text != NULL && *text != '\0') {
2480 /* Render and push */
2481 gst_text_overlay_render_text (overlay, text, -1);
2482 ret = gst_text_overlay_push_frame (overlay, buffer);
2484 /* Invalid or empty string */
2485 ret = gst_pad_push (overlay->srcpad, buffer);
2488 /* Text pad linked, check if we have a text buffer queued */
2489 if (overlay->text_buffer) {
2490 gboolean pop_text = FALSE, valid_text_time = TRUE;
2491 GstClockTime text_start = GST_CLOCK_TIME_NONE;
2492 GstClockTime text_end = GST_CLOCK_TIME_NONE;
2493 GstClockTime text_running_time = GST_CLOCK_TIME_NONE;
2494 GstClockTime text_running_time_end = GST_CLOCK_TIME_NONE;
2495 GstClockTime vid_running_time, vid_running_time_end;
2497 /* if the text buffer isn't stamped right, pop it off the
2498 * queue and display it for the current video frame only */
2499 if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
2500 !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
2501 GST_WARNING_OBJECT (overlay,
2502 "Got text buffer with invalid timestamp or duration");
2504 valid_text_time = FALSE;
2506 text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
2507 text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
2511 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2513 vid_running_time_end =
2514 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2517 /* If timestamp and duration are valid */
2518 if (valid_text_time) {
2520 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2522 text_running_time_end =
2523 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2527 GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2528 GST_TIME_ARGS (text_running_time),
2529 GST_TIME_ARGS (text_running_time_end));
2530 GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
2531 GST_TIME_ARGS (vid_running_time),
2532 GST_TIME_ARGS (vid_running_time_end));
2534 /* Text too old or in the future */
2535 if (valid_text_time && text_running_time_end <= vid_running_time) {
2536 /* text buffer too old, get rid of it and do nothing */
2537 GST_LOG_OBJECT (overlay, "text buffer too old, popping");
2539 gst_text_overlay_pop_text (overlay);
2540 GST_OBJECT_UNLOCK (overlay);
2541 goto wait_for_text_buf;
2542 } else if (valid_text_time && vid_running_time_end <= text_running_time) {
2543 GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
2544 GST_OBJECT_UNLOCK (overlay);
2545 /* Push the video frame */
2546 ret = gst_pad_push (overlay->srcpad, buffer);
2551 in_text = (gchar *) GST_BUFFER_DATA (overlay->text_buffer);
2552 in_size = GST_BUFFER_SIZE (overlay->text_buffer);
2554 /* g_markup_escape_text() absolutely requires valid UTF8 input, it
2555 * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
2556 * here on purpose, this is something that needs fixing upstream */
2557 if (!g_utf8_validate (in_text, in_size, NULL)) {
2558 const gchar *end = NULL;
2560 GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
2561 in_text = g_strndup (in_text, in_size);
2562 while (!g_utf8_validate (in_text, in_size, &end) && end)
2563 *((gchar *) end) = '*';
2566 /* Get the string */
2567 if (overlay->have_pango_markup) {
2568 text = g_strndup (in_text, in_size);
2570 text = g_markup_escape_text (in_text, in_size);
2573 if (text != NULL && *text != '\0') {
2574 gint text_len = strlen (text);
2576 while (text_len > 0 && (text[text_len - 1] == '\n' ||
2577 text[text_len - 1] == '\r')) {
2580 GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
2581 gst_text_overlay_render_text (overlay, text, text_len);
2583 GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
2584 gst_text_overlay_render_text (overlay, " ", 1);
2587 if (in_text != (gchar *) GST_BUFFER_DATA (overlay->text_buffer))
2590 GST_OBJECT_UNLOCK (overlay);
2591 ret = gst_text_overlay_push_frame (overlay, buffer);
2593 if (valid_text_time && text_running_time_end <= vid_running_time_end) {
2594 GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
2599 GST_OBJECT_LOCK (overlay);
2600 gst_text_overlay_pop_text (overlay);
2601 GST_OBJECT_UNLOCK (overlay);
2604 gboolean wait_for_text_buf = TRUE;
2606 if (overlay->text_eos)
2607 wait_for_text_buf = FALSE;
2609 if (!overlay->wait_text)
2610 wait_for_text_buf = FALSE;
2612 /* Text pad linked, but no text buffer available - what now? */
2613 if (overlay->text_segment.format == GST_FORMAT_TIME) {
2614 GstClockTime text_start_running_time, text_last_stop_running_time;
2615 GstClockTime vid_running_time;
2618 gst_segment_to_running_time (&overlay->segment, GST_FORMAT_TIME,
2619 GST_BUFFER_TIMESTAMP (buffer));
2620 text_start_running_time =
2621 gst_segment_to_running_time (&overlay->text_segment,
2622 GST_FORMAT_TIME, overlay->text_segment.start);
2623 text_last_stop_running_time =
2624 gst_segment_to_running_time (&overlay->text_segment,
2625 GST_FORMAT_TIME, overlay->text_segment.last_stop);
2627 if ((GST_CLOCK_TIME_IS_VALID (text_start_running_time) &&
2628 vid_running_time < text_start_running_time) ||
2629 (GST_CLOCK_TIME_IS_VALID (text_last_stop_running_time) &&
2630 vid_running_time < text_last_stop_running_time)) {
2631 wait_for_text_buf = FALSE;
2635 if (wait_for_text_buf) {
2636 GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
2637 GST_TEXT_OVERLAY_WAIT (overlay);
2638 GST_DEBUG_OBJECT (overlay, "resuming");
2639 GST_OBJECT_UNLOCK (overlay);
2640 goto wait_for_text_buf;
2642 GST_OBJECT_UNLOCK (overlay);
2643 GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
2644 ret = gst_pad_push (overlay->srcpad, buffer);
2651 /* Update last_stop */
2652 gst_segment_set_last_stop (&overlay->segment, GST_FORMAT_TIME, clip_start);
2658 GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
2659 gst_buffer_unref (buffer);
2665 GST_OBJECT_UNLOCK (overlay);
2666 GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
2667 gst_buffer_unref (buffer);
2668 return GST_FLOW_WRONG_STATE;
2672 GST_OBJECT_UNLOCK (overlay);
2673 GST_DEBUG_OBJECT (overlay, "eos, discarding buffer");
2674 gst_buffer_unref (buffer);
2675 return GST_FLOW_UNEXPECTED;
2679 GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
2680 gst_buffer_unref (buffer);
2685 static GstStateChangeReturn
2686 gst_text_overlay_change_state (GstElement * element, GstStateChange transition)
2688 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2689 GstTextOverlay *overlay = GST_TEXT_OVERLAY (element);
2691 switch (transition) {
2692 case GST_STATE_CHANGE_PAUSED_TO_READY:
2693 GST_OBJECT_LOCK (overlay);
2694 overlay->text_flushing = TRUE;
2695 overlay->video_flushing = TRUE;
2696 /* pop_text will broadcast on the GCond and thus also make the video
2697 * chain exit if it's waiting for a text buffer */
2698 gst_text_overlay_pop_text (overlay);
2699 GST_OBJECT_UNLOCK (overlay);
2705 ret = parent_class->change_state (element, transition);
2706 if (ret == GST_STATE_CHANGE_FAILURE)
2709 switch (transition) {
2710 case GST_STATE_CHANGE_READY_TO_PAUSED:
2711 GST_OBJECT_LOCK (overlay);
2712 overlay->text_flushing = FALSE;
2713 overlay->video_flushing = FALSE;
2714 overlay->video_eos = FALSE;
2715 overlay->text_eos = FALSE;
2716 gst_segment_init (&overlay->segment, GST_FORMAT_TIME);
2717 gst_segment_init (&overlay->text_segment, GST_FORMAT_TIME);
2718 GST_OBJECT_UNLOCK (overlay);
2728 plugin_init (GstPlugin * plugin)
2730 gst_controller_init (NULL, NULL);
2732 if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
2733 GST_TYPE_TEXT_OVERLAY) ||
2734 !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
2735 GST_TYPE_TIME_OVERLAY) ||
2736 !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
2737 GST_TYPE_CLOCK_OVERLAY) ||
2738 !gst_element_register (plugin, "textrender", GST_RANK_NONE,
2739 GST_TYPE_TEXT_RENDER)) {
2743 /*texttestsrc_plugin_init(module, plugin); */
2745 GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
2750 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
2751 "pango", "Pango-based text rendering and overlay", plugin_init,
2752 VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)