gstreamer/ext/pango/gsttextrender.c
Sebastian Dröge 49deb0c05d Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use static strings (i.e. all). This gives us less memory u...
Original commit message from CVS:
* configure.ac:
* ext/alsa/gstalsamixerelement.c:
(gst_alsa_mixer_element_class_init):
* ext/alsa/gstalsasink.c: (gst_alsasink_class_init):
* ext/alsa/gstalsasrc.c: (gst_alsasrc_class_init):
* ext/cdparanoia/gstcdparanoiasrc.c:
(gst_cd_paranoia_src_class_init):
* ext/gio/gstgiosink.c: (gst_gio_sink_class_init):
* ext/gio/gstgiosrc.c: (gst_gio_src_class_init):
* ext/gio/gstgiostreamsink.c: (gst_gio_stream_sink_class_init):
* ext/gio/gstgiostreamsrc.c: (gst_gio_stream_src_class_init):
* ext/gnomevfs/gstgnomevfssink.c: (gst_gnome_vfs_sink_class_init):
* ext/gnomevfs/gstgnomevfssrc.c: (gst_gnome_vfs_src_class_init):
* ext/ogg/gstoggmux.c: (gst_ogg_mux_class_init):
* ext/pango/gsttextoverlay.c: (gst_text_overlay_class_init):
* ext/pango/gsttextrender.c: (gst_text_render_class_init):
* ext/theora/theoradec.c: (gst_theora_dec_class_init):
* ext/theora/theoraenc.c: (gst_theora_enc_class_init):
* ext/theora/theoraparse.c: (gst_theora_parse_class_init):
* ext/vorbis/vorbisenc.c: (gst_vorbis_enc_class_init):
* gst-libs/gst/audio/gstaudiofiltertemplate.c:
(gst_audio_filter_template_class_init):
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_class_init):
* gst-libs/gst/audio/gstbaseaudiosrc.c:
(gst_base_audio_src_class_init):
* gst-libs/gst/cdda/gstcddabasesrc.c:
(gst_cdda_base_src_class_init):
* gst-libs/gst/interfaces/mixertrack.c:
(gst_mixer_track_class_init):
* gst-libs/gst/rtp/gstbasertpdepayload.c:
(gst_base_rtp_depayload_class_init):
* gst-libs/gst/rtp/gstbasertppayload.c:
(gst_basertppayload_class_init):
* gst/audioconvert/gstaudioconvert.c:
(gst_audio_convert_class_init):
* gst/audiorate/gstaudiorate.c: (gst_audio_rate_class_init):
* gst/audioresample/gstaudioresample.c:
(gst_audioresample_class_init):
* gst/audiotestsrc/gstaudiotestsrc.c:
(gst_audio_test_src_class_init):
* gst/gdp/gstgdppay.c: (gst_gdp_pay_class_init):
* gst/playback/gstdecodebin2.c: (gst_decode_bin_class_init):
* gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init),
(preroll_unlinked):
* gst/playback/gstplaybin.c: (gst_play_bin_class_init):
* gst/playback/gstplaybin2.c: (gst_play_bin_class_init):
* gst/playback/gstplaysink.c: (gst_play_sink_class_init):
* gst/playback/gstqueue2.c: (gst_queue_class_init):
* gst/playback/gststreaminfo.c: (gst_stream_info_class_init):
* gst/playback/gststreamselector.c: (gst_selector_pad_class_init),
(gst_stream_selector_class_init):
* gst/playback/gsturidecodebin.c: (gst_uri_decode_bin_class_init):
* gst/subparse/gstsubparse.c: (gst_sub_parse_class_init):
* gst/tcp/gstmultifdsink.c: (gst_multi_fd_sink_class_init):
* gst/tcp/gsttcpclientsink.c: (gst_tcp_client_sink_class_init):
* gst/tcp/gsttcpclientsrc.c: (gst_tcp_client_src_class_init):
* gst/tcp/gsttcpserversink.c: (gst_tcp_server_sink_class_init):
* gst/tcp/gsttcpserversrc.c: (gst_tcp_server_src_class_init):
* gst/videorate/gstvideorate.c: (gst_video_rate_class_init):
* gst/videoscale/gstvideoscale.c: (gst_video_scale_class_init):
* gst/videotestsrc/gstvideotestsrc.c:
(gst_video_test_src_class_init):
* gst/volume/gstvolume.c: (gst_volume_class_init):
* sys/v4l/gstv4lelement.c: (gst_v4lelement_class_init):
* sys/v4l/gstv4lmjpegsink.c: (gst_v4lmjpegsink_class_init):
* sys/v4l/gstv4lmjpegsrc.c: (gst_v4lmjpegsrc_class_init):
* sys/v4l/gstv4lsrc.c: (gst_v4lsrc_class_init):
* sys/ximage/ximagesink.c: (gst_ximagesink_class_init):
* sys/xvimage/xvimagesink.c: (gst_xvimagesink_class_init):
Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use
static strings (i.e. all). This gives us less memory usage,
fewer allocations and thus less memory defragmentation. Depend
on core CVS for this. Fixes bug #523806.
2008-03-22 15:00:53 +00:00

382 lines
11 KiB
C

/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2003> David Schleef <ds@schleef.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-textrender
* @see_also: #GstTextOverlay
*
* <refsect2>
* <para>
* This plugin renders text received on the text sink pad to a video
* buffer (retaining the alpha channel), so it can later be overlayed
* on top of video streams using other elements.
* </para>
* <para>
* The text can contain newline characters. (FIXME: What about text
* wrapping? It does not make sense in this context)
* </para>
* <para>
* Example pipeline:
* <programlisting>
* gst-launch -v filesrc location=subtitles.srt ! subparse ! textrender ! xvimagesink
* </programlisting>
* </para>
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gsttextrender.h"
GST_DEBUG_CATEGORY_EXTERN (pango_debug);
#define GST_CAT_DEFAULT pango_debug
static const GstElementDetails text_render_details =
GST_ELEMENT_DETAILS ("Text renderer",
"Filter/Editor/Video",
"Renders a text string to an image bitmap",
"David Schleef <ds@schleef.org>, "
"Ronald S. Bultje <rbultje@ronald.bitfreak.net>");
enum
{
ARG_0,
ARG_FONT_DESC
};
static GstStaticPadTemplate src_template_factory =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
);
static GstStaticPadTemplate sink_template_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
);
GST_BOILERPLATE (GstTextRender, gst_text_render, GstElement, GST_TYPE_ELEMENT)
static void gst_text_render_finalize (GObject * object);
static void gst_text_render_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_text_render_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template_factory));
gst_element_class_set_details (element_class, &text_render_details);
}
static void
gst_text_render_class_init (GstTextRenderClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_text_render_finalize;
gobject_class->set_property = gst_text_render_set_property;
klass->pango_context = pango_ft2_get_context (72, 72);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC,
g_param_spec_string ("font-desc", "font description",
"Pango font description of font "
"to be used for rendering. "
"See documentation of "
"pango_font_description_from_string"
" for syntax.", "", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
}
static void
resize_bitmap (GstTextRender * render, gint width, gint height)
{
FT_Bitmap *bitmap = &render->bitmap;
gint pitch = (width | 3) + 1;
gint size = pitch * height;
/* no need to keep reallocating; just keep the maximum size so far */
if (size <= render->bitmap_buffer_size) {
bitmap->rows = height;
bitmap->width = width;
bitmap->pitch = pitch;
memset (bitmap->buffer, 0, render->bitmap_buffer_size);
return;
}
if (!bitmap->buffer) {
/* initialize */
bitmap->pixel_mode = ft_pixel_mode_grays;
bitmap->num_grays = 256;
}
if (bitmap->buffer)
bitmap->buffer = g_realloc (bitmap->buffer, size);
else
bitmap->buffer = g_malloc (size);
bitmap->rows = height;
bitmap->width = width;
bitmap->pitch = pitch;
memset (bitmap->buffer, 0, size);
render->bitmap_buffer_size = size;
}
static void
gst_text_render_render_text (GstTextRender * render)
{
PangoRectangle ink_rect, logical_rect;
pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect);
resize_bitmap (render, ink_rect.width, ink_rect.height + ink_rect.y);
pango_ft2_render_layout (&render->bitmap, render->layout, -ink_rect.x, 0);
render->baseline_y = ink_rect.y;
}
static gboolean
gst_text_render_setcaps (GstPad * pad, GstCaps * caps)
{
GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
GstStructure *structure;
gboolean ret = FALSE;
gint width = 0, height = 0;
structure = gst_caps_get_structure (caps, 0);
gst_structure_get_int (structure, "width", &width);
gst_structure_get_int (structure, "height", &height);
GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps);
if (width >= render->bitmap.width && height >= render->bitmap.rows) {
render->width = width;
render->height = height;
ret = TRUE;
}
gst_object_unref (render);
return ret;
}
static void
gst_text_render_fixate_caps (GstPad * pad, GstCaps * caps)
{
GstTextRender *render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
GstStructure *s = gst_caps_get_structure (caps, 0);
GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps);
gst_structure_fixate_field_nearest_int (s, "width", render->bitmap.width);
gst_structure_fixate_field_nearest_int (s, "height", render->bitmap.rows);
GST_DEBUG ("Fixated to %" GST_PTR_FORMAT, caps);
gst_object_unref (render);
}
static void
gst_text_renderer_bitmap_to_ayuv (GstTextRender * render, FT_Bitmap * bitmap,
guchar * pixbuf)
{
int y; /* text bitmap coordinates */
int rowinc, bit_rowinc;
guchar *p, *bitp;
guchar v;
rowinc = render->width - bitmap->width;
bit_rowinc = bitmap->pitch - bitmap->width;
bitp = bitmap->buffer;
p = pixbuf;
for (y = 0; y < bitmap->rows; y++) {
int n;
for (n = bitmap->width; n > 0; --n) {
v = *bitp;
if (v) {
p[0] = v;
p[1] = 255;
p[2] = 0x80;
p[3] = 0x80;
}
p += 4;
bitp++;
}
p += rowinc * 4;
bitp += bit_rowinc;
}
}
static GstFlowReturn
gst_text_render_chain (GstPad * pad, GstBuffer * inbuf)
{
GstTextRender *render;
GstFlowReturn ret;
GstBuffer *outbuf;
GstCaps *caps = NULL;
guint8 *data = GST_BUFFER_DATA (inbuf);
guint size = GST_BUFFER_SIZE (inbuf);
gint n;
render = GST_TEXT_RENDER (gst_pad_get_parent (pad));
/* somehow pango barfs over "\0" buffers... */
while (size > 0 &&
(data[size - 1] == '\r' ||
data[size - 1] == '\n' || data[size - 1] == '\0')) {
size--;
}
/* render text */
GST_DEBUG ("rendering '%*s'", size, data);
pango_layout_set_markup (render->layout, (gchar *) data, size);
gst_text_render_render_text (render);
caps = gst_caps_new_simple ("video/x-raw-yuv", "format", GST_TYPE_FOURCC,
GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'), "width", G_TYPE_INT,
render->bitmap.width, "height", G_TYPE_INT, render->bitmap.rows,
"framerate", GST_TYPE_FRACTION, 1, 1, NULL);
if (!gst_pad_set_caps (render->srcpad, caps)) {
gst_caps_unref (caps);
GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL));
ret = GST_FLOW_ERROR;
goto done;
}
GST_DEBUG ("Allocating AYUV buffer WxH = %dx%d", render->width,
render->height);
ret =
gst_pad_alloc_buffer_and_set_caps (render->srcpad, GST_BUFFER_OFFSET_NONE,
render->width * render->height * 4, caps, &outbuf);
if (ret != GST_FLOW_OK)
goto done;
gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
data = GST_BUFFER_DATA (outbuf);
for (n = 0; n < render->width * render->height; n++) {
data[n * 4] = 0;
data[n * 4 + 1] = 0;
data[n * 4 + 2] = data[n * 4 + 3] = 128;
}
if (render->bitmap.buffer) {
gst_text_renderer_bitmap_to_ayuv (render, &render->bitmap, data);
}
ret = gst_pad_push (render->srcpad, outbuf);
done:
if (caps)
gst_caps_unref (caps);
gst_buffer_unref (inbuf);
gst_object_unref (render);
return ret;
}
static void
gst_text_render_finalize (GObject * object)
{
GstTextRender *render = GST_TEXT_RENDER (object);
g_free (render->bitmap.buffer);
if (render->layout)
g_object_unref (render->layout);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_text_render_init (GstTextRender * render, GstTextRenderClass * klass)
{
GstPadTemplate *template;
/* sink */
template = gst_static_pad_template_get (&sink_template_factory);
render->sinkpad = gst_pad_new_from_template (template, "sink");
gst_object_unref (template);
gst_pad_set_chain_function (render->sinkpad,
GST_DEBUG_FUNCPTR (gst_text_render_chain));
gst_element_add_pad (GST_ELEMENT (render), render->sinkpad);
/* source */
template = gst_static_pad_template_get (&src_template_factory);
render->srcpad = gst_pad_new_from_template (template, "src");
gst_object_unref (template);
gst_pad_set_fixatecaps_function (render->srcpad,
GST_DEBUG_FUNCPTR (gst_text_render_fixate_caps));
gst_pad_set_setcaps_function (render->srcpad,
GST_DEBUG_FUNCPTR (gst_text_render_setcaps));
gst_element_add_pad (GST_ELEMENT (render), render->srcpad);
render->layout =
pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context);
memset (&render->bitmap, 0, sizeof (render->bitmap));
}
static void
gst_text_render_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstTextRender *render = GST_TEXT_RENDER (object);
switch (prop_id) {
case ARG_FONT_DESC:
{
PangoFontDescription *desc;
desc = pango_font_description_from_string (g_value_get_string (value));
if (desc) {
GST_LOG ("font description set: %s", g_value_get_string (value));
GST_OBJECT_LOCK (render);
pango_layout_set_font_description (render->layout, desc);
pango_font_description_free (desc);
gst_text_render_render_text (render);
GST_OBJECT_UNLOCK (render);
} else {
GST_WARNING ("font description parse failed: %s",
g_value_get_string (value));
}
break;
}
default:
break;
}
}