gst_plugins_base/ext/pango/gsttextrender.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  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Library General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Library General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Library General Public
       
    16  * License along with this library; if not, write to the
       
    17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    18  * Boston, MA 02111-1307, USA.
       
    19  */
       
    20 
       
    21 
       
    22 /**
       
    23  * SECTION:element-textrender
       
    24  * @see_also: #GstTextOverlay
       
    25  *
       
    26  * <refsect2>
       
    27  * <para>
       
    28  * This plugin renders text received on the text sink pad to a video
       
    29  * buffer (retaining the alpha channel), so it can later be overlayed
       
    30  * on top of video streams using other elements.
       
    31  * </para>
       
    32  * <para>
       
    33  * The text can contain newline characters. (FIXME: What about text 
       
    34  * wrapping? It does not make sense in this context)
       
    35  * </para>
       
    36  * <para>
       
    37  * Example pipeline:
       
    38  * <programlisting>
       
    39  * gst-launch -v filesrc location=subtitles.srt ! subparse ! textrender ! xvimagesink
       
    40  * </programlisting>
       
    41  * </para>
       
    42  * </refsect2>
       
    43  */
       
    44 
       
    45 #ifdef HAVE_CONFIG_H
       
    46 #include <config.h>
       
    47 #endif
       
    48 
       
    49 #include <gst/gst.h>
       
    50 #include <gst/video/video.h>
       
    51 
       
    52 #include "gsttextrender.h"
       
    53 
       
    54 GST_DEBUG_CATEGORY_EXTERN (pango_debug);
       
    55 #define GST_CAT_DEFAULT pango_debug
       
    56 
       
    57 static const GstElementDetails text_render_details =
       
    58 GST_ELEMENT_DETAILS ("Text renderer",
       
    59     "Filter/Editor/Video",
       
    60     "Renders a text string to an image bitmap",
       
    61     "David Schleef <ds@schleef.org>, "
       
    62     "Ronald S. Bultje <rbultje@ronald.bitfreak.net>");
       
    63 
       
    64 enum
       
    65 {
       
    66   ARG_0,
       
    67   ARG_FONT_DESC
       
    68 };
       
    69 
       
    70 
       
    71 static GstStaticPadTemplate src_template_factory =
       
    72 GST_STATIC_PAD_TEMPLATE ("src",
       
    73     GST_PAD_SRC,
       
    74     GST_PAD_ALWAYS,
       
    75     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
       
    76     );
       
    77 
       
    78 static GstStaticPadTemplate sink_template_factory =
       
    79     GST_STATIC_PAD_TEMPLATE ("sink",
       
    80     GST_PAD_SINK,
       
    81     GST_PAD_ALWAYS,
       
    82     GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
       
    83     );
       
    84 
       
    85 GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT)
       
    86 
       
    87      static void gst_text_render_finalize (GObject * object);
       
    88      static void gst_text_render_set_property (GObject * object,
       
    89     guint prop_id, const GValue * value, GParamSpec * pspec);
       
    90 
       
    91      static void gst_text_render_base_init (gpointer g_class)
       
    92 {
       
    93   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
    94 
       
    95   gst_element_class_add_pad_template (element_class,
       
    96       gst_static_pad_template_get (&src_template_factory));
       
    97   gst_element_class_add_pad_template (element_class,
       
    98       gst_static_pad_template_get (&sink_template_factory));
       
    99 
       
   100   gst_element_class_set_details (element_class, &text_render_details);
       
   101 }
       
   102 
       
   103 static void
       
   104 gst_text_render_class_init (GstTextRenderClass * klass)
       
   105 {
       
   106   GObjectClass *gobject_class;
       
   107   GstElementClass *gstelement_class;
       
   108 
       
   109   gobject_class = (GObjectClass *) klass;
       
   110   gstelement_class = (GstElementClass *) klass;
       
   111 
       
   112   parent_class = g_type_class_peek_parent (klass);
       
   113 
       
   114   gobject_class->finalize = gst_text_render_finalize;
       
   115   gobject_class->set_property = gst_text_render_set_property;
       
   116 
       
   117   klass->pango_context = pango_ft2_get_context (72, 72);
       
   118   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC,
       
   119       g_param_spec_string ("font-desc", "font description",
       
   120           "Pango font description of font "
       
   121           "to be used for rendering. "
       
   122           "See documentation of "
       
   123           "pango_font_description_from_string"
       
   124           " for syntax.", "", G_PARAM_WRITABLE));
       
   125 }
       
   126 
       
   127 
       
   128 static void
       
   129 resize_bitmap (GstTextRender * render, gint width, gint height)
       
   130 {
       
   131   FT_Bitmap *bitmap = &render->bitmap;
       
   132   gint pitch = (width | 3) + 1;
       
   133   gint size = pitch * height;
       
   134 
       
   135   /* no need to keep reallocating; just keep the maximum size so far */
       
   136   if (size <= render->bitmap_buffer_size) {
       
   137     bitmap->rows = height;
       
   138     bitmap->width = width;
       
   139     bitmap->pitch = pitch;
       
   140     memset (bitmap->buffer, 0, render->bitmap_buffer_size);
       
   141     return;
       
   142   }
       
   143   if (!bitmap->buffer) {
       
   144     /* initialize */
       
   145     bitmap->pixel_mode = ft_pixel_mode_grays;
       
   146     bitmap->num_grays = 256;
       
   147   }
       
   148   if (bitmap->buffer)
       
   149     bitmap->buffer = g_realloc (bitmap->buffer, size);
       
   150   else
       
   151     bitmap->buffer = g_malloc (size);
       
   152   bitmap->rows = height;
       
   153   bitmap->width = width;
       
   154   bitmap->pitch = pitch;
       
   155   memset (bitmap->buffer, 0, size);
       
   156   render->bitmap_buffer_size = size;
       
   157 }
       
   158 
       
   159 static void
       
   160 gst_text_render_render_text (GstTextRender * render)
       
   161 {
       
   162   PangoRectangle ink_rect, logical_rect;
       
   163 
       
   164   pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect);
       
   165   resize_bitmap (render, ink_rect.width, ink_rect.height + ink_rect.y);
       
   166   pango_ft2_render_layout (&render->bitmap, render->layout, -ink_rect.x, 0);
       
   167   render->baseline_y = ink_rect.y;
       
   168 }
       
   169 
       
   170 static gboolean
       
   171 gst_text_render_setcaps (GstPad * pad, GstCaps * caps)
       
   172 {
       
   173   GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
       
   174   GstStructure *structure;
       
   175   gboolean ret = FALSE;
       
   176   gint width = 0, height = 0;
       
   177 
       
   178   structure = gst_caps_get_structure (caps, 0);
       
   179   gst_structure_get_int (structure, "width", &width);
       
   180   gst_structure_get_int (structure, "height", &height);
       
   181 
       
   182   GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps);
       
   183 
       
   184   if (width >= render->bitmap.width && height >= render->bitmap.rows) {
       
   185     render->width = width;
       
   186     render->height = height;
       
   187     ret = TRUE;
       
   188   }
       
   189 
       
   190   gst_object_unref (render);
       
   191   return ret;
       
   192 }
       
   193 
       
   194 static void
       
   195 gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps)
       
   196 {
       
   197   GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
       
   198   GstStructure *s = gst_caps_get_structure (caps, 0);
       
   199 
       
   200   GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps);
       
   201   gst_structure_fixate_field_nearest_int (s, "width", render->bitmap.width);
       
   202   gst_structure_fixate_field_nearest_int (s, "height", render->bitmap.rows);
       
   203   GST_DEBUG ("Fixated to    %" GST_PTR_FORMAT, caps);
       
   204 
       
   205   gst_object_unref (render);
       
   206 }
       
   207 
       
   208 static void
       
   209 gst_text_renderer_bitmap_to_ayuv (GstTextRender * render, FT_Bitmap * bitmap,
       
   210     guchar * pixbuf)
       
   211 {
       
   212   int y;                        /* text bitmap coordinates */
       
   213   int rowinc, bit_rowinc;
       
   214   guchar *p, *bitp;
       
   215   guchar v;
       
   216 
       
   217   rowinc = render->width - bitmap->width;
       
   218   bit_rowinc = bitmap->pitch - bitmap->width;
       
   219 
       
   220   bitp = bitmap->buffer;
       
   221   p = pixbuf;
       
   222 
       
   223   for (y = 0; y < bitmap->rows; y++) {
       
   224     int n;
       
   225 
       
   226     for (n = bitmap->width; n > 0; --n) {
       
   227       v = *bitp;
       
   228       if (v) {
       
   229         p[0] = v;
       
   230         p[1] = 255;
       
   231         p[2] = 0x80;
       
   232         p[3] = 0x80;
       
   233       }
       
   234       p += 4;
       
   235       bitp++;
       
   236     }
       
   237     p += rowinc * 4;
       
   238     bitp += bit_rowinc;
       
   239   }
       
   240 }
       
   241 
       
   242 
       
   243 static GstFlowReturn
       
   244 gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
       
   245 {
       
   246   GstTextRender *render;
       
   247   GstFlowReturn ret;
       
   248   GstBuffer *outbuf;
       
   249   GstCaps *caps = NULL;
       
   250   guint8 *data = GST_BUFFER_DATA (inbuf);
       
   251   guint size = GST_BUFFER_SIZE (inbuf);
       
   252   gint n;
       
   253 
       
   254   render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
       
   255 
       
   256   /* somehow pango barfs over "\0" buffers... */
       
   257   while (size > 0 &&
       
   258       (data[size - 1] == '\r' ||
       
   259           data[size - 1] == '\n' || data[size - 1] == '\0')) {
       
   260     size--;
       
   261   }
       
   262 
       
   263   /* render text */
       
   264   GST_DEBUG ("rendering '%*s'", size, data);
       
   265   pango_layout_set_markup (render->layout, (gchar *) data, size);
       
   266   gst_text_render_render_text (render);
       
   267 
       
   268   caps = gst_caps_new_simple ("video/x-raw-yuv", "format", GST_TYPE_FOURCC,
       
   269       GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'), "width", G_TYPE_INT,
       
   270       render->bitmap.width, "height", G_TYPE_INT, render->bitmap.rows,
       
   271       "framerate", GST_TYPE_FRACTION, 1, 1, NULL);
       
   272 
       
   273   if (!gst_pad_set_caps (render->srcpad, caps)) {
       
   274     gst_caps_unref (caps);
       
   275     GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL));
       
   276     ret = GST_FLOW_ERROR;
       
   277     goto done;
       
   278   }
       
   279 
       
   280   GST_DEBUG ("Allocating AYUV buffer WxH = %dx%d", render->width,
       
   281       render->height);
       
   282   ret =
       
   283       gst_pad_alloc_buffer_and_set_caps (render->srcpad, GST_BUFFER_OFFSET_NONE,
       
   284       render->width * render->height * 4, caps, &outbuf);
       
   285 
       
   286   if (ret != GST_FLOW_OK)
       
   287     goto done;
       
   288 
       
   289   gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
       
   290   data = GST_BUFFER_DATA (outbuf);
       
   291 
       
   292   for (n = 0; n < render->width * render->height; n++) {
       
   293     data[n * 4] = 0;
       
   294     data[n * 4 + 1] = 0;
       
   295     data[n * 4 + 2] = data[n * 4 + 3] = 128;
       
   296   }
       
   297 
       
   298   if (render->bitmap.buffer) {
       
   299     gst_text_renderer_bitmap_to_ayuv (render, &render->bitmap, data);
       
   300   }
       
   301 
       
   302   ret = gst_pad_push (render->srcpad, outbuf);
       
   303 
       
   304 done:
       
   305   if (caps)
       
   306     gst_caps_unref (caps);
       
   307   gst_buffer_unref (inbuf);
       
   308   gst_object_unref (render);
       
   309   return ret;
       
   310 }
       
   311 
       
   312 static void
       
   313 gst_text_render_finalize (GObject * object)
       
   314 {
       
   315   GstTextRender *render = GST_TEXT_RENDER (object);
       
   316 
       
   317   g_free (render->bitmap.buffer);
       
   318 
       
   319   if (render->layout)
       
   320     g_object_unref (render->layout);
       
   321 
       
   322   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   323 }
       
   324 
       
   325 static void
       
   326 gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass)
       
   327 {
       
   328   GstPadTemplate *template;
       
   329 
       
   330   /* sink */
       
   331   template = gst_static_pad_template_get (&sink_template_factory);
       
   332   render->sinkpad = gst_pad_new_from_template (template, "sink");
       
   333   gst_object_unref (template);
       
   334   gst_pad_set_chain_function (render->sinkpad,
       
   335       GST_DEBUG_FUNCPTR (gst_text_render_chain));
       
   336   gst_element_add_pad (GST_ELEMENT (render), render->sinkpad);
       
   337 
       
   338   /* source */
       
   339   template = gst_static_pad_template_get (&src_template_factory);
       
   340   render->srcpad = gst_pad_new_from_template (template, "src");
       
   341   gst_object_unref (template);
       
   342   gst_pad_set_fixatecaps_function (render->srcpad,
       
   343       GST_DEBUG_FUNCPTR (gst_text_render_fixate_caps));
       
   344   gst_pad_set_setcaps_function (render->srcpad,
       
   345       GST_DEBUG_FUNCPTR (gst_text_render_setcaps));
       
   346   gst_element_add_pad (GST_ELEMENT (render), render->srcpad);
       
   347 
       
   348   render->layout =
       
   349       pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context);
       
   350   memset (&render->bitmap, 0, sizeof (render->bitmap));
       
   351 }
       
   352 
       
   353 static void
       
   354 gst_text_render_set_property (GObject * object, guint prop_id,
       
   355     const GValue * value, GParamSpec * pspec)
       
   356 {
       
   357   GstTextRender *render = GST_TEXT_RENDER (object);
       
   358 
       
   359   switch (prop_id) {
       
   360     case ARG_FONT_DESC:
       
   361     {
       
   362       PangoFontDescription *desc;
       
   363 
       
   364       desc = pango_font_description_from_string (g_value_get_string (value));
       
   365       if (desc) {
       
   366         GST_LOG ("font description set: %s", g_value_get_string (value));
       
   367         GST_OBJECT_LOCK (render);
       
   368         pango_layout_set_font_description (render->layout, desc);
       
   369         pango_font_description_free (desc);
       
   370         gst_text_render_render_text (render);
       
   371         GST_OBJECT_UNLOCK (render);
       
   372       } else {
       
   373         GST_WARNING ("font description parse failed: %s",
       
   374             g_value_get_string (value));
       
   375       }
       
   376       break;
       
   377     }
       
   378 
       
   379     default:
       
   380       break;
       
   381   }
       
   382 }