gst_plugins_base/ext/pango/gsttextoverlay.c
branchRCL_3
changeset 30 7e817e7e631c
parent 0 0e761a78d257
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
       
     1 /* GStreamer
       
     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> Tim-Philipp Müller <tim centricular net>
       
     6  *
       
     7  * This library is free software; you can redistribute it and/or
       
     8  * modify it under the terms of the GNU Library General Public
       
     9  * License as published by the Free Software Foundation; either
       
    10  * version 2 of the License, or (at your option) any later version.
       
    11  *
       
    12  * This library is distributed in the hope that it will be useful,
       
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    15  * Library General Public License for more details.
       
    16  *
       
    17  * You should have received a copy of the GNU Library General Public
       
    18  * License along with this library; if not, write to the
       
    19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    20  * Boston, MA 02111-1307, USA.
       
    21  */
       
    22 
       
    23 /**
       
    24  * SECTION:element-textoverlay
       
    25  * @see_also: #GstTextRender, #GstClockOverlay, #GstTimeOverlay, #GstSubParse
       
    26  *
       
    27  * <refsect2>
       
    28  * <para>
       
    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.
       
    35  * </para>
       
    36  * <para>
       
    37  * The text can contain newline characters and text wrapping is enabled by
       
    38  * default.
       
    39  * </para>
       
    40  * <para>
       
    41  * Here is a simple pipeline that displays a static text in the top left
       
    42  * corner of the video picture:
       
    43  * <programlisting>
       
    44  * gst-launch -v videotestsrc ! textoverlay text="Room A" valign=top halign=left ! xvimagesink
       
    45  * </programlisting>
       
    46  * </para>
       
    47  * <para>
       
    48  * Here is another pipeline that displays subtitles from an .srt subtitle
       
    49  * file, centered at the bottom of the picture and with a rectangular shading
       
    50  * around the text in the background:
       
    51  * <programlisting>
       
    52  * gst-launch -v filesrc location=subtitles.srt ! subparse ! txt.   videotestsrc ! timeoverlay ! textoverlay name=txt shaded-background=yes ! xvimagesink
       
    53  * </programlisting>
       
    54  * If you do not have such a subtitle file, create one looking like this
       
    55  * in a text editor:
       
    56  * <programlisting>
       
    57  * 1
       
    58  * 00:00:03,000 --> 00:00:05,000
       
    59  * Hello? (3-5s)
       
    60  *  
       
    61  * 2
       
    62  * 00:00:08,000 --> 00:00:13,000
       
    63  * Yes, this is a subtitle. Don&apos;t
       
    64  * you like it? (8-13s)
       
    65  *  
       
    66  * 3
       
    67  * 00:00:18,826 --> 00:01:02,886
       
    68  * Uh? What are you talking about?
       
    69  * I don&apos;t understand  (18-62s)
       
    70  * </programlisting>
       
    71  * </para>
       
    72  * </refsect2>
       
    73  */
       
    74 
       
    75 /* FIXME: alloc segment as part of instance struct */
       
    76 
       
    77 #ifdef HAVE_CONFIG_H
       
    78 #include <config.h>
       
    79 #endif
       
    80 
       
    81 #include <gst/video/video.h>
       
    82 
       
    83 #include "gsttextoverlay.h"
       
    84 #include "gsttimeoverlay.h"
       
    85 #include "gstclockoverlay.h"
       
    86 #include "gsttextrender.h"
       
    87 
       
    88 /* FIXME:
       
    89  *  - use proper strides and offset for I420
       
    90  *  - if text is wider than the video picture, it does not get
       
    91  *    clipped properly during blitting (if wrapping is disabled)
       
    92  *  - make 'shading_value' a property (or enum:  light/normal/dark/verydark)?
       
    93  */
       
    94 
       
    95 GST_DEBUG_CATEGORY (pango_debug);
       
    96 #define GST_CAT_DEFAULT pango_debug
       
    97 
       
    98 static const GstElementDetails text_overlay_details =
       
    99 GST_ELEMENT_DETAILS ("Text overlay",
       
   100     "Filter/Editor/Video",
       
   101     "Adds text strings on top of a video buffer",
       
   102     "David Schleef <ds@schleef.org>");
       
   103 
       
   104 
       
   105 #define DEFAULT_PROP_TEXT 	""
       
   106 #define DEFAULT_PROP_SHADING	FALSE
       
   107 #define DEFAULT_PROP_VALIGNMENT	GST_TEXT_OVERLAY_VALIGN_BASELINE
       
   108 #define DEFAULT_PROP_HALIGNMENT	GST_TEXT_OVERLAY_HALIGN_CENTER
       
   109 #define DEFAULT_PROP_VALIGN	"baseline"
       
   110 #define DEFAULT_PROP_HALIGN	"center"
       
   111 #define DEFAULT_PROP_XPAD	25
       
   112 #define DEFAULT_PROP_YPAD	25
       
   113 #define DEFAULT_PROP_DELTAX	0
       
   114 #define DEFAULT_PROP_DELTAY	0
       
   115 #define DEFAULT_PROP_WRAP_MODE  GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR
       
   116 #define DEFAULT_PROP_FONT_DESC	""
       
   117 #define DEFAULT_PROP_SILENT	FALSE
       
   118 #define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_OVERLAY_LINE_ALIGN_CENTER
       
   119 
       
   120 /* make a property of me */
       
   121 #define DEFAULT_SHADING_VALUE    -80
       
   122 
       
   123 enum
       
   124 {
       
   125   PROP_0,
       
   126   PROP_TEXT,
       
   127   PROP_SHADING,
       
   128   PROP_VALIGN,                  /* deprecated */
       
   129   PROP_HALIGN,                  /* deprecated */
       
   130   PROP_HALIGNMENT,
       
   131   PROP_VALIGNMENT,
       
   132   PROP_XPAD,
       
   133   PROP_YPAD,
       
   134   PROP_DELTAX,
       
   135   PROP_DELTAY,
       
   136   PROP_WRAP_MODE,
       
   137   PROP_FONT_DESC,
       
   138   PROP_SILENT,
       
   139   PROP_LINE_ALIGNMENT
       
   140 };
       
   141 
       
   142 
       
   143 static GstStaticPadTemplate src_template_factory =
       
   144 GST_STATIC_PAD_TEMPLATE ("src",
       
   145     GST_PAD_SRC,
       
   146     GST_PAD_ALWAYS,
       
   147     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
       
   148     );
       
   149 
       
   150 static GstStaticPadTemplate video_sink_template_factory =
       
   151 GST_STATIC_PAD_TEMPLATE ("video_sink",
       
   152     GST_PAD_SINK,
       
   153     GST_PAD_ALWAYS,
       
   154     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
       
   155     );
       
   156 
       
   157 static GstStaticPadTemplate text_sink_template_factory =
       
   158     GST_STATIC_PAD_TEMPLATE ("text_sink",
       
   159     GST_PAD_SINK,
       
   160     GST_PAD_ALWAYS,
       
   161     GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
       
   162     );
       
   163 
       
   164 
       
   165 #define GST_TYPE_TEXT_OVERLAY_VALIGN (gst_text_overlay_valign_get_type())
       
   166 static GType
       
   167 gst_text_overlay_valign_get_type (void)
       
   168 {
       
   169   static GType text_overlay_valign_type = 0;
       
   170   static const GEnumValue text_overlay_valign[] = {
       
   171     {GST_TEXT_OVERLAY_VALIGN_BASELINE, "baseline", "baseline"},
       
   172     {GST_TEXT_OVERLAY_VALIGN_BOTTOM, "bottom", "bottom"},
       
   173     {GST_TEXT_OVERLAY_VALIGN_TOP, "top", "top"},
       
   174     {0, NULL, NULL},
       
   175   };
       
   176 
       
   177   if (!text_overlay_valign_type) {
       
   178     text_overlay_valign_type =
       
   179         g_enum_register_static ("GstTextOverlayVAlign", text_overlay_valign);
       
   180   }
       
   181   return text_overlay_valign_type;
       
   182 }
       
   183 
       
   184 #define GST_TYPE_TEXT_OVERLAY_HALIGN (gst_text_overlay_halign_get_type())
       
   185 static GType
       
   186 gst_text_overlay_halign_get_type (void)
       
   187 {
       
   188   static GType text_overlay_halign_type = 0;
       
   189   static const GEnumValue text_overlay_halign[] = {
       
   190     {GST_TEXT_OVERLAY_HALIGN_LEFT, "left", "left"},
       
   191     {GST_TEXT_OVERLAY_HALIGN_CENTER, "center", "center"},
       
   192     {GST_TEXT_OVERLAY_HALIGN_RIGHT, "right", "right"},
       
   193     {0, NULL, NULL},
       
   194   };
       
   195 
       
   196   if (!text_overlay_halign_type) {
       
   197     text_overlay_halign_type =
       
   198         g_enum_register_static ("GstTextOverlayHAlign", text_overlay_halign);
       
   199   }
       
   200   return text_overlay_halign_type;
       
   201 }
       
   202 
       
   203 
       
   204 #define GST_TYPE_TEXT_OVERLAY_WRAP_MODE (gst_text_overlay_wrap_mode_get_type())
       
   205 static GType
       
   206 gst_text_overlay_wrap_mode_get_type (void)
       
   207 {
       
   208   static GType text_overlay_wrap_mode_type = 0;
       
   209   static const GEnumValue text_overlay_wrap_mode[] = {
       
   210     {GST_TEXT_OVERLAY_WRAP_MODE_NONE, "none", "none"},
       
   211     {GST_TEXT_OVERLAY_WRAP_MODE_WORD, "word", "word"},
       
   212     {GST_TEXT_OVERLAY_WRAP_MODE_CHAR, "char", "char"},
       
   213     {GST_TEXT_OVERLAY_WRAP_MODE_WORD_CHAR, "wordchar", "wordchar"},
       
   214     {0, NULL, NULL},
       
   215   };
       
   216 
       
   217   if (!text_overlay_wrap_mode_type) {
       
   218     text_overlay_wrap_mode_type =
       
   219         g_enum_register_static ("GstTextOverlayWrapMode",
       
   220         text_overlay_wrap_mode);
       
   221   }
       
   222   return text_overlay_wrap_mode_type;
       
   223 }
       
   224 
       
   225 #define GST_TYPE_TEXT_OVERLAY_LINE_ALIGN (gst_text_overlay_line_align_get_type())
       
   226 static GType
       
   227 gst_text_overlay_line_align_get_type (void)
       
   228 {
       
   229   static GType text_overlay_line_align_type = 0;
       
   230   static const GEnumValue text_overlay_line_align[] = {
       
   231     {GST_TEXT_OVERLAY_LINE_ALIGN_LEFT, "left", "left"},
       
   232     {GST_TEXT_OVERLAY_LINE_ALIGN_CENTER, "center", "center"},
       
   233     {GST_TEXT_OVERLAY_LINE_ALIGN_RIGHT, "right", "right"},
       
   234     {0, NULL, NULL}
       
   235   };
       
   236 
       
   237   if (!text_overlay_line_align_type) {
       
   238     text_overlay_line_align_type =
       
   239         g_enum_register_static ("GstTextOverlayLineAlign",
       
   240         text_overlay_line_align);
       
   241   }
       
   242   return text_overlay_line_align_type;
       
   243 }
       
   244 
       
   245 /* These macros are adapted from videotestsrc.c */
       
   246 #define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
       
   247 #define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
       
   248 #define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)
       
   249 
       
   250 #define I420_Y_OFFSET(w,h) (0)
       
   251 #define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
       
   252 #define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
       
   253 
       
   254 #define I420_SIZE(w,h)     (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
       
   255 
       
   256 #define GST_TEXT_OVERLAY_GET_COND(ov) (((GstTextOverlay *)ov)->cond)
       
   257 #define GST_TEXT_OVERLAY_WAIT(ov)     (g_cond_wait (GST_TEXT_OVERLAY_GET_COND (ov), GST_OBJECT_GET_LOCK (ov)))
       
   258 #define GST_TEXT_OVERLAY_SIGNAL(ov)   (g_cond_signal (GST_TEXT_OVERLAY_GET_COND (ov)))
       
   259 #define GST_TEXT_OVERLAY_BROADCAST(ov)(g_cond_broadcast (GST_TEXT_OVERLAY_GET_COND (ov)))
       
   260 
       
   261 static GstStateChangeReturn gst_text_overlay_change_state (GstElement * element,
       
   262     GstStateChange transition);
       
   263 
       
   264 static GstCaps *gst_text_overlay_getcaps (GstPad * pad);
       
   265 static gboolean gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps);
       
   266 static gboolean gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps);
       
   267 static gboolean gst_text_overlay_src_event (GstPad * pad, GstEvent * event);
       
   268 
       
   269 static gboolean gst_text_overlay_video_event (GstPad * pad, GstEvent * event);
       
   270 static GstFlowReturn gst_text_overlay_video_chain (GstPad * pad,
       
   271     GstBuffer * buffer);
       
   272 
       
   273 static gboolean gst_text_overlay_text_event (GstPad * pad, GstEvent * event);
       
   274 static GstFlowReturn gst_text_overlay_text_chain (GstPad * pad,
       
   275     GstBuffer * buffer);
       
   276 static GstPadLinkReturn gst_text_overlay_text_pad_link (GstPad * pad,
       
   277     GstPad * peer);
       
   278 static void gst_text_overlay_text_pad_unlink (GstPad * pad);
       
   279 static void gst_text_overlay_pop_text (GstTextOverlay * overlay);
       
   280 
       
   281 static void gst_text_overlay_finalize (GObject * object);
       
   282 static void gst_text_overlay_set_property (GObject * object, guint prop_id,
       
   283     const GValue * value, GParamSpec * pspec);
       
   284 static void gst_text_overlay_get_property (GObject * object, guint prop_id,
       
   285     GValue * value, GParamSpec * pspec);
       
   286 
       
   287 GST_BOILERPLATE (GstTextOverlay, gst_text_overlay, GstElement, GST_TYPE_ELEMENT)
       
   288 
       
   289      static void gst_text_overlay_base_init (gpointer g_class)
       
   290 {
       
   291   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   292 
       
   293   gst_element_class_add_pad_template (element_class,
       
   294       gst_static_pad_template_get (&src_template_factory));
       
   295   gst_element_class_add_pad_template (element_class,
       
   296       gst_static_pad_template_get (&video_sink_template_factory));
       
   297 
       
   298   /* ugh */
       
   299   if (!GST_IS_TIME_OVERLAY_CLASS (g_class) &&
       
   300       !GST_IS_CLOCK_OVERLAY_CLASS (g_class)) {
       
   301     gst_element_class_add_pad_template (element_class,
       
   302         gst_static_pad_template_get (&text_sink_template_factory));
       
   303   }
       
   304 
       
   305   gst_element_class_set_details (element_class, &text_overlay_details);
       
   306 }
       
   307 
       
   308 static gchar *
       
   309 gst_text_overlay_get_text (GstTextOverlay * overlay, GstBuffer * video_frame)
       
   310 {
       
   311   return g_strdup (overlay->default_text);
       
   312 }
       
   313 
       
   314 static void
       
   315 gst_text_overlay_class_init (GstTextOverlayClass * klass)
       
   316 {
       
   317   GObjectClass *gobject_class;
       
   318   GstElementClass *gstelement_class;
       
   319 
       
   320   gobject_class = (GObjectClass *) klass;
       
   321   gstelement_class = (GstElementClass *) klass;
       
   322 
       
   323   gobject_class->finalize = gst_text_overlay_finalize;
       
   324   gobject_class->set_property = gst_text_overlay_set_property;
       
   325   gobject_class->get_property = gst_text_overlay_get_property;
       
   326 
       
   327   gstelement_class->change_state =
       
   328       GST_DEBUG_FUNCPTR (gst_text_overlay_change_state);
       
   329 
       
   330   klass->get_text = gst_text_overlay_get_text;
       
   331   klass->pango_context = pango_ft2_get_context (72, 72);
       
   332 
       
   333   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEXT,
       
   334       g_param_spec_string ("text", "text",
       
   335           "Text to be display.", DEFAULT_PROP_TEXT, G_PARAM_READWRITE));
       
   336   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SHADING,
       
   337       g_param_spec_boolean ("shaded-background", "shaded background",
       
   338           "Whether to shade the background under the text area",
       
   339           DEFAULT_PROP_SHADING, G_PARAM_READWRITE));
       
   340   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
       
   341       g_param_spec_enum ("valignment", "vertical alignment",
       
   342           "Vertical alignment of the text",
       
   343           GST_TYPE_TEXT_OVERLAY_VALIGN, DEFAULT_PROP_VALIGNMENT,
       
   344           G_PARAM_READWRITE));
       
   345   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
       
   346       g_param_spec_enum ("halignment", "horizontal alignment",
       
   347           "Horizontal alignment of the text", GST_TYPE_TEXT_OVERLAY_HALIGN,
       
   348           DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE));
       
   349   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
       
   350       g_param_spec_string ("valign", "vertical alignment",
       
   351           "Vertical alignment of the text (deprecated; use valignment)",
       
   352           DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE));
       
   353   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
       
   354       g_param_spec_string ("halign", "horizontal alignment",
       
   355           "Horizontal alignment of the text (deprecated; use halignment)",
       
   356           DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE));
       
   357   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
       
   358       g_param_spec_int ("xpad", "horizontal paddding",
       
   359           "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
       
   360           DEFAULT_PROP_XPAD, G_PARAM_READWRITE));
       
   361   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
       
   362       g_param_spec_int ("ypad", "vertical padding",
       
   363           "Vertical padding when using top/bottom alignment", 0, G_MAXINT,
       
   364           DEFAULT_PROP_YPAD, G_PARAM_READWRITE));
       
   365   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAX,
       
   366       g_param_spec_int ("deltax", "X position modifier",
       
   367           "Shift X position to the left or to the right. Unit is pixels.",
       
   368           G_MININT, G_MAXINT, DEFAULT_PROP_DELTAX, G_PARAM_READWRITE));
       
   369   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELTAY,
       
   370       g_param_spec_int ("deltay", "Y position modifier",
       
   371           "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
       
   372           DEFAULT_PROP_DELTAY, G_PARAM_READWRITE));
       
   373   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WRAP_MODE,
       
   374       g_param_spec_enum ("wrap-mode", "wrap mode",
       
   375           "Whether to wrap the text and if so how.",
       
   376           GST_TYPE_TEXT_OVERLAY_WRAP_MODE, DEFAULT_PROP_WRAP_MODE,
       
   377           G_PARAM_READWRITE));
       
   378   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FONT_DESC,
       
   379       g_param_spec_string ("font-desc", "font description",
       
   380           "Pango font description of font to be used for rendering. "
       
   381           "See documentation of pango_font_description_from_string "
       
   382           "for syntax.", DEFAULT_PROP_FONT_DESC, G_PARAM_WRITABLE));
       
   383   /**
       
   384    * GstTextOverlay:line-alignment
       
   385    *
       
   386    * Alignment of text lines relative to each other (for multi-line text)
       
   387    *
       
   388    * Since: 0.10.15
       
   389    **/
       
   390   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
       
   391       g_param_spec_enum ("line-alignment", "line alignment",
       
   392           "Alignment of text lines relative to each other.",
       
   393           GST_TYPE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
       
   394           G_PARAM_READWRITE));
       
   395   /**
       
   396    * GstTextOverlay:silent
       
   397    *
       
   398    * If set, no text is rendered. Useful to switch off text rendering
       
   399    * temporarily without removing the textoverlay element from the pipeline.
       
   400    *
       
   401    * Since: 0.10.15
       
   402    **/
       
   403   /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
       
   404   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
       
   405       g_param_spec_boolean ("silent", "silent",
       
   406           "Whether to render the text string",
       
   407           DEFAULT_PROP_SILENT, G_PARAM_READWRITE));
       
   408 }
       
   409 
       
   410 static void
       
   411 gst_text_overlay_finalize (GObject * object)
       
   412 {
       
   413   GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
       
   414 
       
   415   g_free (overlay->default_text);
       
   416   g_free (overlay->bitmap.buffer);
       
   417 
       
   418   if (overlay->layout) {
       
   419     g_object_unref (overlay->layout);
       
   420     overlay->layout = NULL;
       
   421   }
       
   422 
       
   423   if (overlay->segment) {
       
   424     gst_segment_free (overlay->segment);
       
   425     overlay->segment = NULL;
       
   426   }
       
   427 
       
   428   if (overlay->text_buffer) {
       
   429     gst_buffer_unref (overlay->text_buffer);
       
   430     overlay->text_buffer = NULL;
       
   431   }
       
   432 
       
   433   if (overlay->cond) {
       
   434     g_cond_free (overlay->cond);
       
   435     overlay->cond = NULL;
       
   436   }
       
   437 
       
   438   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   439 }
       
   440 
       
   441 static void
       
   442 gst_text_overlay_init (GstTextOverlay * overlay, GstTextOverlayClass * klass)
       
   443 {
       
   444   GstPadTemplate *template;
       
   445 
       
   446   /* video sink */
       
   447   template = gst_static_pad_template_get (&video_sink_template_factory);
       
   448   overlay->video_sinkpad = gst_pad_new_from_template (template, "video_sink");
       
   449   gst_object_unref (template);
       
   450   gst_pad_set_getcaps_function (overlay->video_sinkpad,
       
   451       GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
       
   452   gst_pad_set_setcaps_function (overlay->video_sinkpad,
       
   453       GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps));
       
   454   gst_pad_set_event_function (overlay->video_sinkpad,
       
   455       GST_DEBUG_FUNCPTR (gst_text_overlay_video_event));
       
   456   gst_pad_set_chain_function (overlay->video_sinkpad,
       
   457       GST_DEBUG_FUNCPTR (gst_text_overlay_video_chain));
       
   458   gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
       
   459 
       
   460   if (!GST_IS_TIME_OVERLAY_CLASS (klass) && !GST_IS_CLOCK_OVERLAY_CLASS (klass)) {
       
   461     /* text sink */
       
   462     template = gst_static_pad_template_get (&text_sink_template_factory);
       
   463     overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
       
   464     gst_object_unref (template);
       
   465     gst_pad_set_setcaps_function (overlay->text_sinkpad,
       
   466         GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps_txt));
       
   467     gst_pad_set_event_function (overlay->text_sinkpad,
       
   468         GST_DEBUG_FUNCPTR (gst_text_overlay_text_event));
       
   469     gst_pad_set_chain_function (overlay->text_sinkpad,
       
   470         GST_DEBUG_FUNCPTR (gst_text_overlay_text_chain));
       
   471     gst_pad_set_link_function (overlay->text_sinkpad,
       
   472         GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_link));
       
   473     gst_pad_set_unlink_function (overlay->text_sinkpad,
       
   474         GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_unlink));
       
   475     gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
       
   476   }
       
   477 
       
   478   /* (video) source */
       
   479   template = gst_static_pad_template_get (&src_template_factory);
       
   480   overlay->srcpad = gst_pad_new_from_template (template, "src");
       
   481   gst_object_unref (template);
       
   482   gst_pad_set_getcaps_function (overlay->srcpad,
       
   483       GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
       
   484   gst_pad_set_event_function (overlay->srcpad,
       
   485       GST_DEBUG_FUNCPTR (gst_text_overlay_src_event));
       
   486   gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
       
   487 
       
   488   overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
       
   489   overlay->layout =
       
   490       pango_layout_new (GST_TEXT_OVERLAY_GET_CLASS (overlay)->pango_context);
       
   491   pango_layout_set_alignment (overlay->layout,
       
   492       (PangoAlignment) overlay->line_align);
       
   493   memset (&overlay->bitmap, 0, sizeof (overlay->bitmap));
       
   494 
       
   495   overlay->halign = DEFAULT_PROP_HALIGNMENT;
       
   496   overlay->valign = DEFAULT_PROP_VALIGNMENT;
       
   497   overlay->xpad = DEFAULT_PROP_XPAD;
       
   498   overlay->ypad = DEFAULT_PROP_YPAD;
       
   499   overlay->deltax = DEFAULT_PROP_DELTAX;
       
   500   overlay->deltay = DEFAULT_PROP_DELTAY;
       
   501 
       
   502   overlay->wrap_mode = DEFAULT_PROP_WRAP_MODE;
       
   503 
       
   504   overlay->want_shading = DEFAULT_PROP_SHADING;
       
   505   overlay->shading_value = DEFAULT_SHADING_VALUE;
       
   506   overlay->silent = DEFAULT_PROP_SILENT;
       
   507 
       
   508   overlay->default_text = g_strdup (DEFAULT_PROP_TEXT);
       
   509   overlay->need_render = TRUE;
       
   510 
       
   511   overlay->fps_n = 0;
       
   512   overlay->fps_d = 1;
       
   513 
       
   514   overlay->text_buffer = NULL;
       
   515   overlay->text_linked = FALSE;
       
   516   overlay->video_flushing = FALSE;
       
   517   overlay->text_flushing = FALSE;
       
   518   overlay->text_eos = FALSE;
       
   519   overlay->cond = g_cond_new ();
       
   520   overlay->segment = gst_segment_new ();
       
   521   if (overlay->segment) {
       
   522     gst_segment_init (overlay->segment, GST_FORMAT_TIME);
       
   523   } else {
       
   524     GST_WARNING_OBJECT (overlay, "segment creation failed");
       
   525     g_assert_not_reached ();
       
   526   }
       
   527 }
       
   528 
       
   529 static void
       
   530 gst_text_overlay_update_wrap_mode (GstTextOverlay * overlay)
       
   531 {
       
   532   if (overlay->wrap_mode == GST_TEXT_OVERLAY_WRAP_MODE_NONE) {
       
   533     GST_DEBUG_OBJECT (overlay, "Set wrap mode NONE");
       
   534     pango_layout_set_width (overlay->layout, -1);
       
   535   } else {
       
   536     GST_DEBUG_OBJECT (overlay, "Set layout width %d", overlay->width);
       
   537     GST_DEBUG_OBJECT (overlay, "Set wrap mode    %d", overlay->wrap_mode);
       
   538     pango_layout_set_width (overlay->layout, overlay->width * PANGO_SCALE);
       
   539     pango_layout_set_wrap (overlay->layout, (PangoWrapMode) overlay->wrap_mode);
       
   540   }
       
   541 }
       
   542 
       
   543 static gboolean
       
   544 gst_text_overlay_setcaps_txt (GstPad * pad, GstCaps * caps)
       
   545 {
       
   546   GstTextOverlay *overlay;
       
   547   GstStructure *structure;
       
   548 
       
   549   overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
       
   550 
       
   551   structure = gst_caps_get_structure (caps, 0);
       
   552   overlay->have_pango_markup =
       
   553       gst_structure_has_name (structure, "text/x-pango-markup");
       
   554 
       
   555   gst_object_unref (overlay);
       
   556 
       
   557   return TRUE;
       
   558 }
       
   559 
       
   560 /* FIXME: upstream nego (e.g. when the video window is resized) */
       
   561 
       
   562 static gboolean
       
   563 gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps)
       
   564 {
       
   565   GstTextOverlay *overlay;
       
   566   GstStructure *structure;
       
   567   gboolean ret = FALSE;
       
   568   const GValue *fps;
       
   569 
       
   570   if (!GST_PAD_IS_SINK (pad))
       
   571     return TRUE;
       
   572 
       
   573   g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
       
   574 
       
   575   overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
       
   576 
       
   577   overlay->width = 0;
       
   578   overlay->height = 0;
       
   579   structure = gst_caps_get_structure (caps, 0);
       
   580   fps = gst_structure_get_value (structure, "framerate");
       
   581 
       
   582   if (gst_structure_get_int (structure, "width", &overlay->width) &&
       
   583       gst_structure_get_int (structure, "height", &overlay->height) &&
       
   584       fps != NULL) {
       
   585     ret = gst_pad_set_caps (overlay->srcpad, caps);
       
   586   }
       
   587 
       
   588   overlay->fps_n = gst_value_get_fraction_numerator (fps);
       
   589   overlay->fps_d = gst_value_get_fraction_denominator (fps);
       
   590 
       
   591   if (ret) {
       
   592     GST_OBJECT_LOCK (overlay);
       
   593     gst_text_overlay_update_wrap_mode (overlay);
       
   594     GST_OBJECT_UNLOCK (overlay);
       
   595   }
       
   596 
       
   597   gst_object_unref (overlay);
       
   598 
       
   599   return ret;
       
   600 }
       
   601 
       
   602 static void
       
   603 gst_text_overlay_set_property (GObject * object, guint prop_id,
       
   604     const GValue * value, GParamSpec * pspec)
       
   605 {
       
   606   GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
       
   607 
       
   608   GST_OBJECT_LOCK (overlay);
       
   609   switch (prop_id) {
       
   610     case PROP_TEXT:
       
   611       g_free (overlay->default_text);
       
   612       overlay->default_text = g_value_dup_string (value);
       
   613       overlay->need_render = TRUE;
       
   614       break;
       
   615     case PROP_SHADING:
       
   616       overlay->want_shading = g_value_get_boolean (value);
       
   617       break;
       
   618     case PROP_XPAD:
       
   619       overlay->xpad = g_value_get_int (value);
       
   620       break;
       
   621     case PROP_YPAD:
       
   622       overlay->ypad = g_value_get_int (value);
       
   623       break;
       
   624     case PROP_DELTAX:
       
   625       overlay->deltax = g_value_get_int (value);
       
   626       break;
       
   627     case PROP_DELTAY:
       
   628       overlay->deltay = g_value_get_int (value);
       
   629       break;
       
   630     case PROP_HALIGN:{
       
   631       const gchar *s = g_value_get_string (value);
       
   632 
       
   633       if (s && g_ascii_strcasecmp (s, "left") == 0)
       
   634         overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
       
   635       else if (s && g_ascii_strcasecmp (s, "center") == 0)
       
   636         overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
       
   637       else if (s && g_ascii_strcasecmp (s, "right") == 0)
       
   638         overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
       
   639       else
       
   640         g_warning ("Invalid value '%s' for textoverlay property 'halign'",
       
   641             GST_STR_NULL (s));
       
   642       break;
       
   643     }
       
   644     case PROP_VALIGN:{
       
   645       const gchar *s = g_value_get_string (value);
       
   646 
       
   647       if (s && g_ascii_strcasecmp (s, "baseline") == 0)
       
   648         overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
       
   649       else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
       
   650         overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM;
       
   651       else if (s && g_ascii_strcasecmp (s, "top") == 0)
       
   652         overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
       
   653       else
       
   654         g_warning ("Invalid value '%s' for textoverlay property 'valign'",
       
   655             GST_STR_NULL (s));
       
   656       break;
       
   657     }
       
   658     case PROP_VALIGNMENT:
       
   659       overlay->valign = g_value_get_enum (value);
       
   660       break;
       
   661     case PROP_HALIGNMENT:
       
   662       overlay->halign = g_value_get_enum (value);
       
   663       break;
       
   664     case PROP_WRAP_MODE:
       
   665       overlay->wrap_mode = g_value_get_enum (value);
       
   666       gst_text_overlay_update_wrap_mode (overlay);
       
   667       break;
       
   668     case PROP_FONT_DESC:
       
   669     {
       
   670       PangoFontDescription *desc;
       
   671       const gchar *fontdesc_str;
       
   672 
       
   673       fontdesc_str = g_value_get_string (value);
       
   674       desc = pango_font_description_from_string (fontdesc_str);
       
   675       if (desc) {
       
   676         GST_LOG_OBJECT (overlay, "font description set: %s", fontdesc_str);
       
   677         pango_layout_set_font_description (overlay->layout, desc);
       
   678         pango_font_description_free (desc);
       
   679       } else {
       
   680         GST_WARNING_OBJECT (overlay, "font description parse failed: %s",
       
   681             fontdesc_str);
       
   682       }
       
   683       break;
       
   684     }
       
   685     case PROP_SILENT:
       
   686       overlay->silent = g_value_get_boolean (value);
       
   687       break;
       
   688     case PROP_LINE_ALIGNMENT:
       
   689       overlay->line_align = g_value_get_enum (value);
       
   690       pango_layout_set_alignment (overlay->layout,
       
   691           (PangoAlignment) overlay->line_align);
       
   692       break;
       
   693     default:
       
   694       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   695       break;
       
   696   }
       
   697 
       
   698   overlay->need_render = TRUE;
       
   699   GST_OBJECT_UNLOCK (overlay);
       
   700 }
       
   701 
       
   702 static void
       
   703 gst_text_overlay_get_property (GObject * object, guint prop_id,
       
   704     GValue * value, GParamSpec * pspec)
       
   705 {
       
   706   GstTextOverlay *overlay = GST_TEXT_OVERLAY (object);
       
   707 
       
   708   GST_OBJECT_LOCK (overlay);
       
   709   switch (prop_id) {
       
   710     case PROP_TEXT:
       
   711       g_value_set_string (value, overlay->default_text);
       
   712       break;
       
   713     case PROP_SHADING:
       
   714       g_value_set_boolean (value, overlay->want_shading);
       
   715       break;
       
   716     case PROP_XPAD:
       
   717       g_value_set_int (value, overlay->xpad);
       
   718       break;
       
   719     case PROP_YPAD:
       
   720       g_value_set_int (value, overlay->ypad);
       
   721       break;
       
   722     case PROP_DELTAX:
       
   723       g_value_set_int (value, overlay->deltax);
       
   724       break;
       
   725     case PROP_DELTAY:
       
   726       g_value_set_int (value, overlay->deltay);
       
   727       break;
       
   728     case PROP_VALIGNMENT:
       
   729       g_value_set_enum (value, overlay->valign);
       
   730       break;
       
   731     case PROP_HALIGNMENT:
       
   732       g_value_set_enum (value, overlay->halign);
       
   733       break;
       
   734     case PROP_WRAP_MODE:
       
   735       g_value_set_enum (value, overlay->wrap_mode);
       
   736       break;
       
   737     case PROP_SILENT:
       
   738       g_value_set_boolean (value, overlay->silent);
       
   739       break;
       
   740     case PROP_LINE_ALIGNMENT:
       
   741       g_value_set_enum (value, overlay->line_align);
       
   742       break;
       
   743     default:
       
   744       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   745       break;
       
   746   }
       
   747 
       
   748   overlay->need_render = TRUE;
       
   749   GST_OBJECT_UNLOCK (overlay);
       
   750 }
       
   751 
       
   752 static gboolean
       
   753 gst_text_overlay_src_event (GstPad * pad, GstEvent * event)
       
   754 {
       
   755   gboolean ret = FALSE;
       
   756   GstTextOverlay *overlay = NULL;
       
   757 
       
   758   overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
       
   759 
       
   760   switch (GST_EVENT_TYPE (event)) {
       
   761     case GST_EVENT_SEEK:
       
   762       /* We don't handle seek if we have not text pad */
       
   763       if (!overlay->text_linked) {
       
   764         ret = gst_pad_push_event (overlay->video_sinkpad, event);
       
   765         goto beach;
       
   766       }
       
   767 
       
   768       GST_DEBUG_OBJECT (overlay, "seek received, driving from here");
       
   769 
       
   770       /* Flush downstream, FIXME, only for flushing seek */
       
   771       gst_pad_push_event (overlay->srcpad, gst_event_new_flush_start ());
       
   772 
       
   773       /* Mark ourself as flushing, unblock chains */
       
   774       GST_OBJECT_LOCK (overlay);
       
   775       overlay->video_flushing = TRUE;
       
   776       overlay->text_flushing = TRUE;
       
   777       gst_text_overlay_pop_text (overlay);
       
   778       GST_OBJECT_UNLOCK (overlay);
       
   779 
       
   780       /* Seek on each sink pad */
       
   781       gst_event_ref (event);
       
   782       ret = gst_pad_push_event (overlay->video_sinkpad, event);
       
   783       if (ret) {
       
   784         ret = gst_pad_push_event (overlay->text_sinkpad, event);
       
   785       } else {
       
   786         gst_event_unref (event);
       
   787       }
       
   788       break;
       
   789     default:
       
   790       if (overlay->text_linked) {
       
   791         gst_event_ref (event);
       
   792         ret = gst_pad_push_event (overlay->video_sinkpad, event);
       
   793         gst_pad_push_event (overlay->text_sinkpad, event);
       
   794       } else {
       
   795         ret = gst_pad_push_event (overlay->video_sinkpad, event);
       
   796       }
       
   797       break;
       
   798   }
       
   799 
       
   800 beach:
       
   801   gst_object_unref (overlay);
       
   802 
       
   803   return ret;
       
   804 }
       
   805 
       
   806 static GstCaps *
       
   807 gst_text_overlay_getcaps (GstPad * pad)
       
   808 {
       
   809   GstTextOverlay *overlay;
       
   810   GstPad *otherpad;
       
   811   GstCaps *caps;
       
   812 
       
   813   overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
       
   814 
       
   815   if (pad == overlay->srcpad)
       
   816     otherpad = overlay->video_sinkpad;
       
   817   else
       
   818     otherpad = overlay->srcpad;
       
   819 
       
   820   /* we can do what the peer can */
       
   821   caps = gst_pad_peer_get_caps (otherpad);
       
   822   if (caps) {
       
   823     GstCaps *temp;
       
   824     const GstCaps *templ;
       
   825 
       
   826     GST_DEBUG_OBJECT (pad, "peer caps  %" GST_PTR_FORMAT, caps);
       
   827 
       
   828     /* filtered against our padtemplate */
       
   829     templ = gst_pad_get_pad_template_caps (otherpad);
       
   830     GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, templ);
       
   831     temp = gst_caps_intersect (caps, templ);
       
   832     GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
       
   833     gst_caps_unref (caps);
       
   834     /* this is what we can do */
       
   835     caps = temp;
       
   836   } else {
       
   837     /* no peer, our padtemplate is enough then */
       
   838     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
       
   839   }
       
   840 
       
   841   GST_DEBUG_OBJECT (overlay, "returning  %" GST_PTR_FORMAT, caps);
       
   842 
       
   843   gst_object_unref (overlay);
       
   844 
       
   845   return caps;
       
   846 }
       
   847 
       
   848 #define BOX_XPAD         6
       
   849 #define BOX_YPAD         6
       
   850 
       
   851 static inline void
       
   852 gst_text_overlay_shade_y (GstTextOverlay * overlay, guchar * dest,
       
   853     guint dest_stride, gint x0, gint x1, gint y0, gint y1)
       
   854 {
       
   855   gint i, j;
       
   856 
       
   857   x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
       
   858   x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
       
   859 
       
   860   y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
       
   861   y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
       
   862 
       
   863   for (i = y0; i < y1; ++i) {
       
   864     for (j = x0; j < x1; ++j) {
       
   865       gint y = dest[(i * dest_stride) + j] + overlay->shading_value;
       
   866 
       
   867       dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
       
   868     }
       
   869   }
       
   870 }
       
   871 
       
   872 /* FIXME:
       
   873  *  - use proper strides and offset for I420
       
   874  *  - don't draw over the edge of the picture (try a longer
       
   875  *    text with a huge font size)
       
   876  */
       
   877 
       
   878 static inline void
       
   879 gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
       
   880     guint8 * yuv_pixels, gint x0, gint y0)
       
   881 {
       
   882   int y;                        /* text bitmap coordinates */
       
   883   int x1, y1;                   /* video buffer coordinates */
       
   884   int bit_rowinc, uv_rowinc;
       
   885   guint8 *p, *bitp, *u_p;
       
   886   int video_width, video_height;
       
   887   int bitmap_x0 = 0;            //x0 < 1 ? -(x0 - 1) : 1;       /* 1 pixel border */
       
   888   int bitmap_y0 = y0 < 1 ? -(y0 - 1) : 1;       /* 1 pixel border */
       
   889   int bitmap_width = bitmap->width - bitmap_x0;
       
   890   int bitmap_height = bitmap->rows - bitmap_y0;
       
   891   int u_plane_size;
       
   892   int skip_y, skip_x;
       
   893   guint8 v;
       
   894 
       
   895   video_width = I420_Y_ROWSTRIDE (overlay->width);
       
   896   video_height = overlay->height;
       
   897 
       
   898 /*
       
   899   if (x0 < 0 && abs (x0) < bitmap_width) {
       
   900     bitmap_x0 = abs (x0);
       
   901     x0 = 0;
       
   902   }
       
   903 */
       
   904 
       
   905   if (x0 + bitmap_x0 + bitmap_width > overlay->width - 1)       /* 1 pixel border */
       
   906     bitmap_width -= x0 + bitmap_x0 + bitmap_width - overlay->width + 1;
       
   907   if (y0 + bitmap_y0 + bitmap_height > video_height - 1)        /* 1 pixel border */
       
   908     bitmap_height -= y0 + bitmap_y0 + bitmap_height - video_height + 1;
       
   909 
       
   910   uv_rowinc = video_width / 2 - bitmap_width / 2;
       
   911   bit_rowinc = bitmap->pitch - bitmap_width;
       
   912   u_plane_size = (video_width / 2) * (video_height / 2);
       
   913 
       
   914   y1 = y0 + bitmap_y0;
       
   915   x1 = x0 + bitmap_x0;
       
   916   bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
       
   917   for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
       
   918     int n;
       
   919 
       
   920     p = yuv_pixels + (y + y0) * I420_Y_ROWSTRIDE (overlay->width) + x1;
       
   921     for (n = bitmap_width; n > 0; --n) {
       
   922       v = *bitp;
       
   923       if (v) {
       
   924         p[-1] = CLAMP (p[-1] - v, 0, 255);
       
   925         p[1] = CLAMP (p[1] - v, 0, 255);
       
   926         p[-video_width] = CLAMP (p[-video_width] - v, 0, 255);
       
   927         p[video_width] = CLAMP (p[video_width] - v, 0, 255);
       
   928       }
       
   929       p++;
       
   930       bitp++;
       
   931     }
       
   932     bitp += bit_rowinc;
       
   933   }
       
   934 
       
   935   y = bitmap_y0;
       
   936   y1 = y0 + bitmap_y0;
       
   937   x1 = x0 + bitmap_x0;
       
   938   bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
       
   939   p = yuv_pixels + video_width * y1 + x1;
       
   940   u_p =
       
   941       yuv_pixels + video_width * video_height + (video_width >> 1) * (y1 >> 1) +
       
   942       (x1 >> 1);
       
   943   skip_y = 0;
       
   944   skip_x = 0;
       
   945 
       
   946   for (; y < bitmap_y0 + bitmap_height; y++) {
       
   947     int n;
       
   948 
       
   949     x1 = x0 + bitmap_x0;
       
   950     skip_x = 0;
       
   951     for (n = bitmap_width; n > 0; --n) {
       
   952       v = *bitp;
       
   953       if (v) {
       
   954         *p = v;
       
   955         if (!skip_y) {
       
   956           u_p[0] = u_p[u_plane_size] = 0x80;
       
   957         }
       
   958       }
       
   959       if (!skip_y) {
       
   960         skip_x = !skip_x;
       
   961         if (!skip_x)
       
   962           u_p++;
       
   963       }
       
   964       p++;
       
   965       bitp++;
       
   966     }
       
   967     /*if (!skip_x && !skip_y) u_p--; */
       
   968     p += I420_Y_ROWSTRIDE (overlay->width) - bitmap_width;
       
   969     bitp += bit_rowinc;
       
   970     skip_y = !skip_y;
       
   971     u_p += skip_y ? uv_rowinc : 0;
       
   972   }
       
   973 }
       
   974 
       
   975 static void
       
   976 gst_text_overlay_resize_bitmap (GstTextOverlay * overlay, gint width,
       
   977     gint height)
       
   978 {
       
   979   FT_Bitmap *bitmap = &overlay->bitmap;
       
   980   int pitch = (width | 3) + 1;
       
   981   int size = pitch * height;
       
   982 
       
   983   /* no need to keep reallocating; just keep the maximum size so far */
       
   984   if (size <= overlay->bitmap_buffer_size) {
       
   985     bitmap->rows = height;
       
   986     bitmap->width = width;
       
   987     bitmap->pitch = pitch;
       
   988     memset (bitmap->buffer, 0, overlay->bitmap_buffer_size);
       
   989     return;
       
   990   }
       
   991   if (!bitmap->buffer) {
       
   992     /* initialize */
       
   993     bitmap->pixel_mode = ft_pixel_mode_grays;
       
   994     bitmap->num_grays = 256;
       
   995   }
       
   996   overlay->bitmap_buffer_size = size;
       
   997   bitmap->buffer = g_realloc (bitmap->buffer, size);
       
   998   memset (bitmap->buffer, 0, size);
       
   999   bitmap->rows = height;
       
  1000   bitmap->width = width;
       
  1001   bitmap->pitch = pitch;
       
  1002 }
       
  1003 
       
  1004 static void
       
  1005 gst_text_overlay_render_text (GstTextOverlay * overlay,
       
  1006     const gchar * text, gint textlen)
       
  1007 {
       
  1008   PangoRectangle ink_rect, logical_rect;
       
  1009   gchar *string;
       
  1010 
       
  1011   if (!overlay->need_render) {
       
  1012     GST_DEBUG ("Using previously rendered text.");
       
  1013     return;
       
  1014   }
       
  1015 
       
  1016   /* -1 is the whole string */
       
  1017   if (text != NULL && textlen < 0) {
       
  1018     textlen = strlen (text);
       
  1019   }
       
  1020 
       
  1021   if (text != NULL) {
       
  1022     string = g_strndup (text, textlen);
       
  1023   } else {                      /* empty string */
       
  1024     string = g_strdup (" ");
       
  1025   }
       
  1026   g_strdelimit (string, "\r\t", ' ');
       
  1027   textlen = strlen (string);
       
  1028 
       
  1029   /* FIXME: should we check for UTF-8 here? */
       
  1030 
       
  1031   GST_DEBUG ("Rendering '%s'", string);
       
  1032   pango_layout_set_markup (overlay->layout, string, textlen);
       
  1033 
       
  1034   pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
       
  1035   gst_text_overlay_resize_bitmap (overlay, ink_rect.width,
       
  1036       ink_rect.height + ink_rect.y);
       
  1037   pango_ft2_render_layout (&overlay->bitmap, overlay->layout, -ink_rect.x, 0);
       
  1038   overlay->baseline_y = ink_rect.y;
       
  1039 
       
  1040   g_free (string);
       
  1041 
       
  1042   overlay->need_render = FALSE;
       
  1043 }
       
  1044 
       
  1045 static GstFlowReturn
       
  1046 gst_text_overlay_push_frame (GstTextOverlay * overlay, GstBuffer * video_frame)
       
  1047 {
       
  1048   gint xpos, ypos;
       
  1049 
       
  1050   video_frame = gst_buffer_make_writable (video_frame);
       
  1051 
       
  1052   switch (overlay->halign) {
       
  1053     case GST_TEXT_OVERLAY_HALIGN_LEFT:
       
  1054       xpos = overlay->xpad;
       
  1055       break;
       
  1056     case GST_TEXT_OVERLAY_HALIGN_CENTER:
       
  1057       xpos = (overlay->width - overlay->bitmap.width) / 2;
       
  1058       break;
       
  1059     case GST_TEXT_OVERLAY_HALIGN_RIGHT:
       
  1060       xpos = overlay->width - overlay->bitmap.width - overlay->xpad;
       
  1061       break;
       
  1062     default:
       
  1063       xpos = 0;
       
  1064   }
       
  1065   xpos += overlay->deltax;
       
  1066 
       
  1067 
       
  1068   switch (overlay->valign) {
       
  1069     case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
       
  1070       ypos = overlay->height - overlay->bitmap.rows - overlay->ypad;
       
  1071       break;
       
  1072     case GST_TEXT_OVERLAY_VALIGN_BASELINE:
       
  1073       ypos = overlay->height - (overlay->bitmap.rows + overlay->ypad);
       
  1074       break;
       
  1075     case GST_TEXT_OVERLAY_VALIGN_TOP:
       
  1076       ypos = overlay->ypad;
       
  1077       break;
       
  1078     default:
       
  1079       ypos = overlay->ypad;
       
  1080       break;
       
  1081   }
       
  1082   ypos += overlay->deltay;
       
  1083 
       
  1084   /* shaded background box */
       
  1085   if (overlay->want_shading) {
       
  1086     gst_text_overlay_shade_y (overlay,
       
  1087         GST_BUFFER_DATA (video_frame),
       
  1088         I420_Y_ROWSTRIDE (overlay->width),
       
  1089         xpos, xpos + overlay->bitmap.width, ypos, ypos + overlay->bitmap.rows);
       
  1090   }
       
  1091 
       
  1092 
       
  1093   if (overlay->bitmap.buffer) {
       
  1094     gst_text_overlay_blit_yuv420 (overlay, &overlay->bitmap,
       
  1095         GST_BUFFER_DATA (video_frame), xpos, ypos);
       
  1096   }
       
  1097 
       
  1098   return gst_pad_push (overlay->srcpad, video_frame);
       
  1099 }
       
  1100 
       
  1101 static GstPadLinkReturn
       
  1102 gst_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
       
  1103 {
       
  1104   GstTextOverlay *overlay;
       
  1105 
       
  1106   overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
       
  1107 
       
  1108   GST_DEBUG_OBJECT (overlay, "Text pad linked");
       
  1109 
       
  1110   overlay->text_linked = TRUE;
       
  1111 
       
  1112   gst_object_unref (overlay);
       
  1113 
       
  1114   return GST_PAD_LINK_OK;
       
  1115 }
       
  1116 
       
  1117 static void
       
  1118 gst_text_overlay_text_pad_unlink (GstPad * pad)
       
  1119 {
       
  1120   GstTextOverlay *overlay;
       
  1121 
       
  1122   /* don't use gst_pad_get_parent() here, will deadlock */
       
  1123   overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
       
  1124 
       
  1125   GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
       
  1126 
       
  1127   overlay->text_linked = FALSE;
       
  1128 
       
  1129   gst_segment_init (&overlay->text_segment, GST_FORMAT_UNDEFINED);
       
  1130 }
       
  1131 
       
  1132 static gboolean
       
  1133 gst_text_overlay_text_event (GstPad * pad, GstEvent * event)
       
  1134 {
       
  1135   gboolean ret = FALSE;
       
  1136   GstTextOverlay *overlay = NULL;
       
  1137 
       
  1138   overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
       
  1139 
       
  1140   GST_LOG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
       
  1141 
       
  1142   switch (GST_EVENT_TYPE (event)) {
       
  1143     case GST_EVENT_NEWSEGMENT:{
       
  1144       GstFormat fmt;
       
  1145       gboolean update;
       
  1146       gdouble rate, applied_rate;
       
  1147       gint64 cur, stop, time;
       
  1148 
       
  1149       overlay->text_eos = FALSE;
       
  1150 
       
  1151       gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
       
  1152           &fmt, &cur, &stop, &time);
       
  1153 
       
  1154       if (fmt == GST_FORMAT_TIME) {
       
  1155         GST_OBJECT_LOCK (overlay);
       
  1156         gst_segment_set_newsegment_full (&overlay->text_segment, update, rate,
       
  1157             applied_rate, GST_FORMAT_TIME, cur, stop, time);
       
  1158         GST_DEBUG_OBJECT (overlay, "TEXT SEGMENT now: %" GST_SEGMENT_FORMAT,
       
  1159             &overlay->text_segment);
       
  1160         GST_OBJECT_UNLOCK (overlay);
       
  1161       } else {
       
  1162         GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
       
  1163             ("received non-TIME newsegment event on text input"));
       
  1164       }
       
  1165 
       
  1166       gst_event_unref (event);
       
  1167       ret = TRUE;
       
  1168 
       
  1169       /* wake up the video chain, it might be waiting for a text buffer or
       
  1170        * a text segment update */
       
  1171       GST_OBJECT_LOCK (overlay);
       
  1172       GST_TEXT_OVERLAY_BROADCAST (overlay);
       
  1173       GST_OBJECT_UNLOCK (overlay);
       
  1174       break;
       
  1175     }
       
  1176     case GST_EVENT_FLUSH_STOP:
       
  1177       GST_OBJECT_LOCK (overlay);
       
  1178       overlay->text_flushing = FALSE;
       
  1179       gst_text_overlay_pop_text (overlay);
       
  1180       GST_OBJECT_UNLOCK (overlay);
       
  1181       gst_event_unref (event);
       
  1182       ret = TRUE;
       
  1183       break;
       
  1184     case GST_EVENT_FLUSH_START:
       
  1185       GST_OBJECT_LOCK (overlay);
       
  1186       overlay->text_flushing = TRUE;
       
  1187       GST_TEXT_OVERLAY_BROADCAST (overlay);
       
  1188       GST_OBJECT_UNLOCK (overlay);
       
  1189       gst_event_unref (event);
       
  1190       ret = TRUE;
       
  1191       break;
       
  1192     case GST_EVENT_EOS:
       
  1193       GST_OBJECT_LOCK (overlay);
       
  1194       overlay->text_flushing = TRUE;
       
  1195       overlay->text_eos = TRUE;
       
  1196       GST_INFO_OBJECT (overlay, "EOS");
       
  1197       /* wake up the video chain, it might be waiting for a text buffer or
       
  1198        * a text segment update */
       
  1199       GST_TEXT_OVERLAY_BROADCAST (overlay);
       
  1200       GST_OBJECT_UNLOCK (overlay);
       
  1201       gst_event_unref (event);
       
  1202       ret = TRUE;
       
  1203       break;
       
  1204     default:
       
  1205       ret = gst_pad_event_default (pad, event);
       
  1206       break;
       
  1207   }
       
  1208 
       
  1209   gst_object_unref (overlay);
       
  1210 
       
  1211   return ret;
       
  1212 }
       
  1213 
       
  1214 static gboolean
       
  1215 gst_text_overlay_video_event (GstPad * pad, GstEvent * event)
       
  1216 {
       
  1217   gboolean ret = FALSE;
       
  1218   GstTextOverlay *overlay = NULL;
       
  1219 
       
  1220   overlay = GST_TEXT_OVERLAY (gst_pad_get_parent (pad));
       
  1221 
       
  1222   GST_DEBUG_OBJECT (pad, "received event %s", GST_EVENT_TYPE_NAME (event));
       
  1223 
       
  1224   switch (GST_EVENT_TYPE (event)) {
       
  1225     case GST_EVENT_NEWSEGMENT:
       
  1226     {
       
  1227       GstFormat format;
       
  1228       gdouble rate;
       
  1229       gint64 start, stop, time;
       
  1230       gboolean update;
       
  1231 
       
  1232       GST_DEBUG_OBJECT (overlay, "received new segment");
       
  1233 
       
  1234       gst_event_parse_new_segment (event, &update, &rate, &format, &start,
       
  1235           &stop, &time);
       
  1236 
       
  1237       if (format == GST_FORMAT_TIME) {
       
  1238         GST_DEBUG_OBJECT (overlay, "VIDEO SEGMENT now: %" GST_SEGMENT_FORMAT,
       
  1239             overlay->segment);
       
  1240 
       
  1241         gst_segment_set_newsegment (overlay->segment, update, rate, format,
       
  1242             start, stop, time);
       
  1243       } else {
       
  1244         GST_ELEMENT_WARNING (overlay, STREAM, MUX, (NULL),
       
  1245             ("received non-TIME newsegment event on video input"));
       
  1246       }
       
  1247 
       
  1248       ret = gst_pad_event_default (pad, event);
       
  1249       break;
       
  1250     }
       
  1251     case GST_EVENT_EOS:
       
  1252       GST_OBJECT_LOCK (overlay);
       
  1253       overlay->video_flushing = TRUE;
       
  1254       overlay->text_flushing = TRUE;
       
  1255       gst_text_overlay_pop_text (overlay);
       
  1256       GST_OBJECT_UNLOCK (overlay);
       
  1257       ret = gst_pad_event_default (pad, event);
       
  1258       break;
       
  1259     case GST_EVENT_FLUSH_START:
       
  1260       GST_OBJECT_LOCK (overlay);
       
  1261       overlay->video_flushing = TRUE;
       
  1262       GST_TEXT_OVERLAY_BROADCAST (overlay);
       
  1263       GST_OBJECT_UNLOCK (overlay);
       
  1264       ret = gst_pad_event_default (pad, event);
       
  1265       break;
       
  1266     case GST_EVENT_FLUSH_STOP:
       
  1267       GST_OBJECT_LOCK (overlay);
       
  1268       overlay->video_flushing = FALSE;
       
  1269       GST_OBJECT_UNLOCK (overlay);
       
  1270       ret = gst_pad_event_default (pad, event);
       
  1271       break;
       
  1272     default:
       
  1273       ret = gst_pad_event_default (pad, event);
       
  1274       break;
       
  1275   }
       
  1276 
       
  1277   gst_object_unref (overlay);
       
  1278 
       
  1279   return ret;
       
  1280 }
       
  1281 
       
  1282 /* Called with lock held */
       
  1283 static void
       
  1284 gst_text_overlay_pop_text (GstTextOverlay * overlay)
       
  1285 {
       
  1286   g_return_if_fail (GST_IS_TEXT_OVERLAY (overlay));
       
  1287 
       
  1288   if (overlay->text_buffer) {
       
  1289     /* update text_segment's last stop */
       
  1290     if (overlay->text_segment.format == GST_FORMAT_TIME &&
       
  1291         GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer)) {
       
  1292       overlay->text_segment.last_stop =
       
  1293           GST_BUFFER_TIMESTAMP (overlay->text_buffer);
       
  1294       if (GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
       
  1295         overlay->text_segment.last_stop +=
       
  1296             GST_BUFFER_DURATION (overlay->text_buffer);
       
  1297       }
       
  1298     }
       
  1299     GST_DEBUG_OBJECT (overlay, "releasing text buffer %p",
       
  1300         overlay->text_buffer);
       
  1301     gst_buffer_unref (overlay->text_buffer);
       
  1302     overlay->text_buffer = NULL;
       
  1303   }
       
  1304 
       
  1305   /* Let the text task know we used that buffer */
       
  1306   GST_TEXT_OVERLAY_BROADCAST (overlay);
       
  1307 }
       
  1308 
       
  1309 /* We receive text buffers here. If they are out of segment we just ignore them.
       
  1310    If the buffer is in our segment we keep it internally except if another one
       
  1311    is already waiting here, in that case we wait that it gets kicked out */
       
  1312 static GstFlowReturn
       
  1313 gst_text_overlay_text_chain (GstPad * pad, GstBuffer * buffer)
       
  1314 {
       
  1315   GstFlowReturn ret = GST_FLOW_OK;
       
  1316   GstTextOverlay *overlay = NULL;
       
  1317   gboolean in_seg = FALSE;
       
  1318   gint64 clip_start = 0, clip_stop = 0;
       
  1319 
       
  1320   overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
       
  1321 
       
  1322   GST_OBJECT_LOCK (overlay);
       
  1323 
       
  1324   if (overlay->text_eos) {
       
  1325     GST_OBJECT_UNLOCK (overlay);
       
  1326     ret = GST_FLOW_UNEXPECTED;
       
  1327     GST_LOG_OBJECT (overlay, "text EOS");
       
  1328     goto beach;
       
  1329   }
       
  1330 
       
  1331   if (overlay->text_flushing) {
       
  1332     GST_OBJECT_UNLOCK (overlay);
       
  1333     ret = GST_FLOW_WRONG_STATE;
       
  1334     GST_LOG_OBJECT (overlay, "text flushing");
       
  1335     goto beach;
       
  1336   }
       
  1337 
       
  1338   GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT "  BUFFER: ts=%"
       
  1339       GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, overlay->segment,
       
  1340       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
       
  1341       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) +
       
  1342           GST_BUFFER_DURATION (buffer)));
       
  1343 
       
  1344   in_seg = gst_segment_clip (overlay->segment, GST_FORMAT_TIME,
       
  1345       GST_BUFFER_TIMESTAMP (buffer),
       
  1346       GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer),
       
  1347       &clip_start, &clip_stop);
       
  1348 
       
  1349   if (in_seg) {
       
  1350     GST_BUFFER_TIMESTAMP (buffer) = clip_start;
       
  1351     GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
       
  1352 
       
  1353     /* Wait for the previous buffer to go away */
       
  1354     while (overlay->text_buffer != NULL) {
       
  1355       GST_DEBUG ("Pad %s:%s has a buffer queued, waiting",
       
  1356           GST_DEBUG_PAD_NAME (pad));
       
  1357       GST_TEXT_OVERLAY_WAIT (overlay);
       
  1358       GST_DEBUG ("Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad));
       
  1359       if (overlay->text_flushing) {
       
  1360         GST_OBJECT_UNLOCK (overlay);
       
  1361         ret = GST_FLOW_WRONG_STATE;
       
  1362         goto beach;
       
  1363       }
       
  1364     }
       
  1365 
       
  1366     overlay->text_buffer = buffer;
       
  1367     /* That's a new text buffer we need to render */
       
  1368     overlay->need_render = TRUE;
       
  1369 
       
  1370     /* in case the video chain is waiting for a text buffer, wake it up */
       
  1371     GST_TEXT_OVERLAY_BROADCAST (overlay);
       
  1372   }
       
  1373 
       
  1374   GST_OBJECT_UNLOCK (overlay);
       
  1375 
       
  1376 beach:
       
  1377 
       
  1378   return ret;
       
  1379 }
       
  1380 
       
  1381 static GstFlowReturn
       
  1382 gst_text_overlay_video_chain (GstPad * pad, GstBuffer * buffer)
       
  1383 {
       
  1384   GstTextOverlayClass *klass;
       
  1385   GstTextOverlay *overlay;
       
  1386   GstFlowReturn ret = GST_FLOW_OK;
       
  1387   gboolean in_seg = FALSE;
       
  1388   gint64 start, stop, clip_start = 0, clip_stop = 0;
       
  1389   gchar *text = NULL;
       
  1390 
       
  1391   overlay = GST_TEXT_OVERLAY (GST_PAD_PARENT (pad));
       
  1392   klass = GST_TEXT_OVERLAY_GET_CLASS (overlay);
       
  1393 
       
  1394   if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
       
  1395     goto missing_timestamp;
       
  1396 
       
  1397   /* ignore buffers that are outside of the current segment */
       
  1398   start = GST_BUFFER_TIMESTAMP (buffer);
       
  1399 
       
  1400   if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
       
  1401     stop = GST_CLOCK_TIME_NONE;
       
  1402   } else {
       
  1403     stop = start + GST_BUFFER_DURATION (buffer);
       
  1404   }
       
  1405 
       
  1406   GST_LOG_OBJECT (overlay, "%" GST_SEGMENT_FORMAT "  BUFFER: ts=%"
       
  1407       GST_TIME_FORMAT ", end=%" GST_TIME_FORMAT, overlay->segment,
       
  1408       GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
       
  1409 
       
  1410   /* segment_clip() will adjust start unconditionally to segment_start if
       
  1411    * no stop time is provided, so handle this ourselves */
       
  1412   if (stop == GST_CLOCK_TIME_NONE && start < overlay->segment->start)
       
  1413     goto out_of_segment;
       
  1414 
       
  1415   in_seg = gst_segment_clip (overlay->segment, GST_FORMAT_TIME, start, stop,
       
  1416       &clip_start, &clip_stop);
       
  1417 
       
  1418   if (!in_seg)
       
  1419     goto out_of_segment;
       
  1420 
       
  1421   /* if the buffer is only partially in the segment, fix up stamps */
       
  1422   if (clip_start != start || (stop != -1 && clip_stop != stop)) {
       
  1423     GST_DEBUG_OBJECT (overlay, "clipping buffer timestamp/duration to segment");
       
  1424     buffer = gst_buffer_make_metadata_writable (buffer);
       
  1425     GST_BUFFER_TIMESTAMP (buffer) = clip_start;
       
  1426     if (stop != -1)
       
  1427       GST_BUFFER_DURATION (buffer) = clip_stop - clip_start;
       
  1428   }
       
  1429 
       
  1430   /* now, after we've done the clipping, fix up end time if there's no
       
  1431    * duration (we only use those estimated values internally though, we
       
  1432    * don't want to set bogus values on the buffer itself) */
       
  1433   if (stop == -1) {
       
  1434     GstStructure *s;
       
  1435     gint fps_num, fps_denom;
       
  1436 
       
  1437     s = gst_caps_get_structure (GST_PAD_CAPS (pad), 0);
       
  1438     if (gst_structure_get_fraction (s, "framerate", &fps_num, &fps_denom)) {
       
  1439       GST_DEBUG_OBJECT (overlay, "estimating duration based on framerate");
       
  1440       stop = start + gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
       
  1441     } else {
       
  1442       GST_WARNING_OBJECT (overlay, "no duration, assuming minimal duration");
       
  1443       stop = start + 1;         /* we need to assume some interval */
       
  1444     }
       
  1445   }
       
  1446 
       
  1447 wait_for_text_buf:
       
  1448 
       
  1449   GST_OBJECT_LOCK (overlay);
       
  1450 
       
  1451   if (overlay->video_flushing)
       
  1452     goto flushing;
       
  1453 
       
  1454   if (overlay->silent) {
       
  1455     GST_OBJECT_UNLOCK (overlay);
       
  1456     ret = gst_pad_push (overlay->srcpad, buffer);
       
  1457 
       
  1458     /* Update last_stop */
       
  1459     gst_segment_set_last_stop (overlay->segment, GST_FORMAT_TIME, clip_start);
       
  1460 
       
  1461     return ret;
       
  1462   }
       
  1463 
       
  1464   /* Text pad not linked, rendering internal text */
       
  1465   if (!overlay->text_linked) {
       
  1466     if (klass->get_text) {
       
  1467       text = klass->get_text (overlay, buffer);
       
  1468     } else {
       
  1469       text = g_strdup (overlay->default_text);
       
  1470     }
       
  1471 
       
  1472     GST_LOG_OBJECT (overlay, "Text pad not linked, rendering default "
       
  1473         "text: '%s'", GST_STR_NULL (text));
       
  1474 
       
  1475     GST_OBJECT_UNLOCK (overlay);
       
  1476 
       
  1477     if (text != NULL && *text != '\0') {
       
  1478       /* Render and push */
       
  1479       gst_text_overlay_render_text (overlay, text, -1);
       
  1480       ret = gst_text_overlay_push_frame (overlay, buffer);
       
  1481     } else {
       
  1482       /* Invalid or empty string */
       
  1483       ret = gst_pad_push (overlay->srcpad, buffer);
       
  1484     }
       
  1485   } else {
       
  1486     /* Text pad linked, check if we have a text buffer queued */
       
  1487     if (overlay->text_buffer) {
       
  1488       gboolean pop_text = FALSE;
       
  1489       gint64 text_start, text_end;
       
  1490 
       
  1491       /* if the text buffer isn't stamped right, pop it off the
       
  1492        * queue and display it for the current video frame only */
       
  1493       if (!GST_BUFFER_TIMESTAMP_IS_VALID (overlay->text_buffer) ||
       
  1494           !GST_BUFFER_DURATION_IS_VALID (overlay->text_buffer)) {
       
  1495         GST_WARNING_OBJECT (overlay,
       
  1496             "Got text buffer with invalid timestamp or duration");
       
  1497         text_start = start;
       
  1498         text_end = stop;
       
  1499         pop_text = TRUE;
       
  1500       } else {
       
  1501         text_start = GST_BUFFER_TIMESTAMP (overlay->text_buffer);
       
  1502         text_end = text_start + GST_BUFFER_DURATION (overlay->text_buffer);
       
  1503       }
       
  1504 
       
  1505       GST_LOG_OBJECT (overlay, "T: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
       
  1506           GST_TIME_ARGS (text_start), GST_TIME_ARGS (text_end));
       
  1507       GST_LOG_OBJECT (overlay, "V: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
       
  1508           GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
       
  1509 
       
  1510       /* Text too old or in the future */
       
  1511       if (text_end <= start) {
       
  1512         /* text buffer too old, get rid of it and do nothing  */
       
  1513         GST_LOG_OBJECT (overlay, "text buffer too old, popping");
       
  1514         pop_text = FALSE;
       
  1515         gst_text_overlay_pop_text (overlay);
       
  1516         GST_OBJECT_UNLOCK (overlay);
       
  1517         goto wait_for_text_buf;
       
  1518       } else if (stop <= text_start) {
       
  1519         GST_LOG_OBJECT (overlay, "text in future, pushing video buf");
       
  1520         GST_OBJECT_UNLOCK (overlay);
       
  1521         /* Push the video frame */
       
  1522         ret = gst_pad_push (overlay->srcpad, buffer);
       
  1523       } else {
       
  1524         gchar *in_text;
       
  1525         gsize in_size;
       
  1526 
       
  1527         in_text = (gchar *) GST_BUFFER_DATA (overlay->text_buffer);
       
  1528         in_size = GST_BUFFER_SIZE (overlay->text_buffer);
       
  1529 
       
  1530         /* g_markup_escape_text() absolutely requires valid UTF8 input, it
       
  1531          * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
       
  1532          * here on purpose, this is something that needs fixing upstream */
       
  1533         if (!g_utf8_validate (in_text, in_size, NULL)) {
       
  1534           const gchar *end = NULL;
       
  1535 
       
  1536           GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
       
  1537           in_text = g_strndup (in_text, in_size);
       
  1538           while (!g_utf8_validate (in_text, in_size, &end) && end)
       
  1539             *((gchar *) end) = '*';
       
  1540         }
       
  1541 
       
  1542         /* Get the string */
       
  1543         if (overlay->have_pango_markup) {
       
  1544           text = g_strndup (in_text, in_size);
       
  1545         } else {
       
  1546           text = g_markup_escape_text (in_text, in_size);
       
  1547         }
       
  1548 
       
  1549         if (text != NULL && *text != '\0') {
       
  1550           gint text_len = strlen (text);
       
  1551 
       
  1552           while (text_len > 0 && (text[text_len - 1] == '\n' ||
       
  1553                   text[text_len - 1] == '\r')) {
       
  1554             --text_len;
       
  1555           }
       
  1556           GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
       
  1557           gst_text_overlay_render_text (overlay, text, text_len);
       
  1558         } else {
       
  1559           GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
       
  1560           gst_text_overlay_render_text (overlay, " ", 1);
       
  1561         }
       
  1562 
       
  1563         if (in_text != (gchar *) GST_BUFFER_DATA (overlay->text_buffer))
       
  1564           g_free (in_text);
       
  1565 
       
  1566         GST_OBJECT_UNLOCK (overlay);
       
  1567         ret = gst_text_overlay_push_frame (overlay, buffer);
       
  1568 
       
  1569         if (text_end <= stop) {
       
  1570           GST_LOG_OBJECT (overlay, "text buffer not needed any longer");
       
  1571           pop_text = TRUE;
       
  1572         }
       
  1573       }
       
  1574       if (pop_text) {
       
  1575         GST_OBJECT_LOCK (overlay);
       
  1576         gst_text_overlay_pop_text (overlay);
       
  1577         GST_OBJECT_UNLOCK (overlay);
       
  1578       }
       
  1579     } else {
       
  1580       gboolean wait_for_text_buf = TRUE;
       
  1581 
       
  1582       if (overlay->text_eos)
       
  1583         wait_for_text_buf = FALSE;
       
  1584 
       
  1585       /* Text pad linked, but no text buffer available - what now? */
       
  1586       if (overlay->text_segment.format == GST_FORMAT_TIME) {
       
  1587         if (GST_BUFFER_TIMESTAMP (buffer) < overlay->text_segment.start ||
       
  1588             GST_BUFFER_TIMESTAMP (buffer) < overlay->text_segment.last_stop) {
       
  1589           wait_for_text_buf = FALSE;
       
  1590         }
       
  1591       }
       
  1592 
       
  1593       if (wait_for_text_buf) {
       
  1594         GST_DEBUG_OBJECT (overlay, "no text buffer, need to wait for one");
       
  1595         GST_TEXT_OVERLAY_WAIT (overlay);
       
  1596         GST_DEBUG_OBJECT (overlay, "resuming");
       
  1597         GST_OBJECT_UNLOCK (overlay);
       
  1598         goto wait_for_text_buf;
       
  1599       } else {
       
  1600         GST_OBJECT_UNLOCK (overlay);
       
  1601         GST_LOG_OBJECT (overlay, "no need to wait for a text buffer");
       
  1602         ret = gst_pad_push (overlay->srcpad, buffer);
       
  1603       }
       
  1604     }
       
  1605   }
       
  1606 
       
  1607   g_free (text);
       
  1608 
       
  1609   /* Update last_stop */
       
  1610   gst_segment_set_last_stop (overlay->segment, GST_FORMAT_TIME, clip_start);
       
  1611 
       
  1612   return ret;
       
  1613 
       
  1614 missing_timestamp:
       
  1615   {
       
  1616     GST_WARNING_OBJECT (overlay, "buffer without timestamp, discarding");
       
  1617     gst_buffer_unref (buffer);
       
  1618     return GST_FLOW_OK;
       
  1619   }
       
  1620 
       
  1621 flushing:
       
  1622   {
       
  1623     GST_OBJECT_UNLOCK (overlay);
       
  1624     GST_DEBUG_OBJECT (overlay, "flushing, discarding buffer");
       
  1625     gst_buffer_unref (buffer);
       
  1626     return GST_FLOW_WRONG_STATE;
       
  1627   }
       
  1628 
       
  1629 out_of_segment:
       
  1630   {
       
  1631     GST_DEBUG_OBJECT (overlay, "buffer out of segment, discarding");
       
  1632     gst_buffer_unref (buffer);
       
  1633     return GST_FLOW_OK;
       
  1634   }
       
  1635 }
       
  1636 
       
  1637 static GstStateChangeReturn
       
  1638 gst_text_overlay_change_state (GstElement * element, GstStateChange transition)
       
  1639 {
       
  1640   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
       
  1641   GstTextOverlay *overlay = GST_TEXT_OVERLAY (element);
       
  1642 
       
  1643   switch (transition) {
       
  1644     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
  1645       GST_OBJECT_LOCK (overlay);
       
  1646       overlay->text_flushing = TRUE;
       
  1647       overlay->video_flushing = TRUE;
       
  1648       /* pop_text will broadcast on the GCond and thus also make the video
       
  1649        * chain exit if it's waiting for a text buffer */
       
  1650       gst_text_overlay_pop_text (overlay);
       
  1651       GST_OBJECT_UNLOCK (overlay);
       
  1652       break;
       
  1653     default:
       
  1654       break;
       
  1655   }
       
  1656 
       
  1657   ret = parent_class->change_state (element, transition);
       
  1658   if (ret == GST_STATE_CHANGE_FAILURE)
       
  1659     return ret;
       
  1660 
       
  1661   switch (transition) {
       
  1662     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
  1663       GST_OBJECT_LOCK (overlay);
       
  1664       overlay->text_flushing = FALSE;
       
  1665       overlay->video_flushing = FALSE;
       
  1666       GST_OBJECT_UNLOCK (overlay);
       
  1667       break;
       
  1668     default:
       
  1669       break;
       
  1670   }
       
  1671 
       
  1672   return ret;
       
  1673 }
       
  1674 
       
  1675 static gboolean
       
  1676 plugin_init (GstPlugin * plugin)
       
  1677 {
       
  1678   if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
       
  1679           GST_TYPE_TEXT_OVERLAY) ||
       
  1680       !gst_element_register (plugin, "timeoverlay", GST_RANK_NONE,
       
  1681           GST_TYPE_TIME_OVERLAY) ||
       
  1682       !gst_element_register (plugin, "clockoverlay", GST_RANK_NONE,
       
  1683           GST_TYPE_CLOCK_OVERLAY) ||
       
  1684       !gst_element_register (plugin, "textrender", GST_RANK_NONE,
       
  1685           GST_TYPE_TEXT_RENDER)) {
       
  1686     return FALSE;
       
  1687   }
       
  1688 
       
  1689   /*texttestsrc_plugin_init(module, plugin); */
       
  1690 
       
  1691   GST_DEBUG_CATEGORY_INIT (pango_debug, "pango", 0, "Pango elements");
       
  1692 
       
  1693   return TRUE;
       
  1694 }
       
  1695 
       
  1696 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
       
  1697     "pango", "Pango-based text rendering and overlay", plugin_init,
       
  1698     VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)