gstreamer/ext/pango/gsttextrender.c

753 lines
22 KiB
C
Raw Normal View History

/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2003> David Schleef <ds@schleef.org>
* Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-textrender
* @see_also: #GstTextOverlay
*
* 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.
Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipe... Original commit message from CVS: * docs/plugins/gst-plugins-base-plugins-docs.sgml: * docs/plugins/gst-plugins-base-plugins-overrides.txt: * docs/plugins/gst-plugins-base-plugins-sections.txt: * docs/plugins/gst-plugins-base-plugins.args: * docs/plugins/gst-plugins-base-plugins.hierarchy: * docs/plugins/gst-plugins-base-plugins.interfaces: * docs/plugins/gst-plugins-base-plugins.prerequisites: * docs/plugins/gst-plugins-base-plugins.signals: * docs/plugins/inspect/plugin-adder.xml: * docs/plugins/inspect/plugin-alsa.xml: * docs/plugins/inspect/plugin-audioconvert.xml: * docs/plugins/inspect/plugin-audiorate.xml: * docs/plugins/inspect/plugin-audioresample.xml: * docs/plugins/inspect/plugin-audiotestsrc.xml: * docs/plugins/inspect/plugin-cdparanoia.xml: * docs/plugins/inspect/plugin-decodebin.xml: * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: * docs/plugins/inspect/plugin-gdp.xml: * docs/plugins/inspect/plugin-gnomevfs.xml: * docs/plugins/inspect/plugin-libvisual.xml: * docs/plugins/inspect/plugin-ogg.xml: * docs/plugins/inspect/plugin-pango.xml: * docs/plugins/inspect/plugin-playback.xml: * docs/plugins/inspect/plugin-queue2.xml: * docs/plugins/inspect/plugin-subparse.xml: * docs/plugins/inspect/plugin-tcp.xml: * docs/plugins/inspect/plugin-theora.xml: * docs/plugins/inspect/plugin-typefindfunctions.xml: * docs/plugins/inspect/plugin-uridecodebin.xml: * docs/plugins/inspect/plugin-video4linux.xml: * docs/plugins/inspect/plugin-videorate.xml: * docs/plugins/inspect/plugin-videoscale.xml: * docs/plugins/inspect/plugin-videotestsrc.xml: * docs/plugins/inspect/plugin-volume.xml: * docs/plugins/inspect/plugin-vorbis.xml: * docs/plugins/inspect/plugin-ximagesink.xml: * docs/plugins/inspect/plugin-xvimagesink.xml: * ext/alsa/gstalsamixer.c: * ext/alsa/gstalsasink.c: * ext/alsa/gstalsasrc.c: * ext/gio/gstgiosink.c: * ext/gio/gstgiosrc.c: * ext/gio/gstgiostreamsink.c: * ext/gio/gstgiostreamsrc.c: * ext/gnomevfs/gstgnomevfssink.c: * ext/gnomevfs/gstgnomevfssrc.c: * ext/ogg/gstoggdemux.c: * ext/ogg/gstoggmux.c: * ext/pango/gstclockoverlay.c: * ext/pango/gsttextoverlay.c: * ext/pango/gsttextrender.c: * ext/pango/gsttimeoverlay.c: * ext/theora/theoradec.c: * ext/theora/theoraenc.c: * ext/theora/theoraparse.c: * ext/vorbis/vorbisdec.c: * ext/vorbis/vorbisenc.c: * ext/vorbis/vorbisparse.c: * ext/vorbis/vorbistag.c: * gst/adder/gstadder.c: * gst/audioconvert/gstaudioconvert.c: * gst/audioresample/gstaudioresample.c: * gst/audiotestsrc/gstaudiotestsrc.c: * gst/ffmpegcolorspace/gstffmpegcolorspace.c: * gst/gdp/gstgdpdepay.c: * gst/gdp/gstgdppay.c: * gst/playback/gstdecodebin2.c: * gst/playback/gstplaybin.c: * gst/playback/gstplaybin2.c: * gst/playback/gstqueue2.c: * gst/playback/gsturidecodebin.c: * gst/tcp/gstmultifdsink.c: * gst/tcp/gsttcpserversink.c: * gst/videorate/gstvideorate.c: * gst/videoscale/gstvideoscale.c: * gst/videotestsrc/gstvideotestsrc.c: * gst/volume/gstvolume.c: * sys/ximage/ximagesink.c: * sys/xvimage/xvimagesink.c: Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipelines are "simple" pipelines.
2008-07-10 21:06:06 +00:00
*
* The text can contain newline characters. (FIXME: What about text
* wrapping? It does not make sense in this context)
Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipe... Original commit message from CVS: * docs/plugins/gst-plugins-base-plugins-docs.sgml: * docs/plugins/gst-plugins-base-plugins-overrides.txt: * docs/plugins/gst-plugins-base-plugins-sections.txt: * docs/plugins/gst-plugins-base-plugins.args: * docs/plugins/gst-plugins-base-plugins.hierarchy: * docs/plugins/gst-plugins-base-plugins.interfaces: * docs/plugins/gst-plugins-base-plugins.prerequisites: * docs/plugins/gst-plugins-base-plugins.signals: * docs/plugins/inspect/plugin-adder.xml: * docs/plugins/inspect/plugin-alsa.xml: * docs/plugins/inspect/plugin-audioconvert.xml: * docs/plugins/inspect/plugin-audiorate.xml: * docs/plugins/inspect/plugin-audioresample.xml: * docs/plugins/inspect/plugin-audiotestsrc.xml: * docs/plugins/inspect/plugin-cdparanoia.xml: * docs/plugins/inspect/plugin-decodebin.xml: * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: * docs/plugins/inspect/plugin-gdp.xml: * docs/plugins/inspect/plugin-gnomevfs.xml: * docs/plugins/inspect/plugin-libvisual.xml: * docs/plugins/inspect/plugin-ogg.xml: * docs/plugins/inspect/plugin-pango.xml: * docs/plugins/inspect/plugin-playback.xml: * docs/plugins/inspect/plugin-queue2.xml: * docs/plugins/inspect/plugin-subparse.xml: * docs/plugins/inspect/plugin-tcp.xml: * docs/plugins/inspect/plugin-theora.xml: * docs/plugins/inspect/plugin-typefindfunctions.xml: * docs/plugins/inspect/plugin-uridecodebin.xml: * docs/plugins/inspect/plugin-video4linux.xml: * docs/plugins/inspect/plugin-videorate.xml: * docs/plugins/inspect/plugin-videoscale.xml: * docs/plugins/inspect/plugin-videotestsrc.xml: * docs/plugins/inspect/plugin-volume.xml: * docs/plugins/inspect/plugin-vorbis.xml: * docs/plugins/inspect/plugin-ximagesink.xml: * docs/plugins/inspect/plugin-xvimagesink.xml: * ext/alsa/gstalsamixer.c: * ext/alsa/gstalsasink.c: * ext/alsa/gstalsasrc.c: * ext/gio/gstgiosink.c: * ext/gio/gstgiosrc.c: * ext/gio/gstgiostreamsink.c: * ext/gio/gstgiostreamsrc.c: * ext/gnomevfs/gstgnomevfssink.c: * ext/gnomevfs/gstgnomevfssrc.c: * ext/ogg/gstoggdemux.c: * ext/ogg/gstoggmux.c: * ext/pango/gstclockoverlay.c: * ext/pango/gsttextoverlay.c: * ext/pango/gsttextrender.c: * ext/pango/gsttimeoverlay.c: * ext/theora/theoradec.c: * ext/theora/theoraenc.c: * ext/theora/theoraparse.c: * ext/vorbis/vorbisdec.c: * ext/vorbis/vorbisenc.c: * ext/vorbis/vorbisparse.c: * ext/vorbis/vorbistag.c: * gst/adder/gstadder.c: * gst/audioconvert/gstaudioconvert.c: * gst/audioresample/gstaudioresample.c: * gst/audiotestsrc/gstaudiotestsrc.c: * gst/ffmpegcolorspace/gstffmpegcolorspace.c: * gst/gdp/gstgdpdepay.c: * gst/gdp/gstgdppay.c: * gst/playback/gstdecodebin2.c: * gst/playback/gstplaybin.c: * gst/playback/gstplaybin2.c: * gst/playback/gstqueue2.c: * gst/playback/gsturidecodebin.c: * gst/tcp/gstmultifdsink.c: * gst/tcp/gsttcpserversink.c: * gst/videorate/gstvideorate.c: * gst/videoscale/gstvideoscale.c: * gst/videotestsrc/gstvideotestsrc.c: * gst/volume/gstvolume.c: * sys/ximage/ximagesink.c: * sys/xvimage/xvimagesink.c: Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipelines are "simple" pipelines.
2008-07-10 21:06:06 +00:00
*
* <refsect2>
* <title>Example launch lines</title>
* |[
* gst-launch-1.0 -v filesrc location=subtitles.srt ! subparse ! textrender ! videoconvert ! autovideosink
Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipe... Original commit message from CVS: * docs/plugins/gst-plugins-base-plugins-docs.sgml: * docs/plugins/gst-plugins-base-plugins-overrides.txt: * docs/plugins/gst-plugins-base-plugins-sections.txt: * docs/plugins/gst-plugins-base-plugins.args: * docs/plugins/gst-plugins-base-plugins.hierarchy: * docs/plugins/gst-plugins-base-plugins.interfaces: * docs/plugins/gst-plugins-base-plugins.prerequisites: * docs/plugins/gst-plugins-base-plugins.signals: * docs/plugins/inspect/plugin-adder.xml: * docs/plugins/inspect/plugin-alsa.xml: * docs/plugins/inspect/plugin-audioconvert.xml: * docs/plugins/inspect/plugin-audiorate.xml: * docs/plugins/inspect/plugin-audioresample.xml: * docs/plugins/inspect/plugin-audiotestsrc.xml: * docs/plugins/inspect/plugin-cdparanoia.xml: * docs/plugins/inspect/plugin-decodebin.xml: * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: * docs/plugins/inspect/plugin-gdp.xml: * docs/plugins/inspect/plugin-gnomevfs.xml: * docs/plugins/inspect/plugin-libvisual.xml: * docs/plugins/inspect/plugin-ogg.xml: * docs/plugins/inspect/plugin-pango.xml: * docs/plugins/inspect/plugin-playback.xml: * docs/plugins/inspect/plugin-queue2.xml: * docs/plugins/inspect/plugin-subparse.xml: * docs/plugins/inspect/plugin-tcp.xml: * docs/plugins/inspect/plugin-theora.xml: * docs/plugins/inspect/plugin-typefindfunctions.xml: * docs/plugins/inspect/plugin-uridecodebin.xml: * docs/plugins/inspect/plugin-video4linux.xml: * docs/plugins/inspect/plugin-videorate.xml: * docs/plugins/inspect/plugin-videoscale.xml: * docs/plugins/inspect/plugin-videotestsrc.xml: * docs/plugins/inspect/plugin-volume.xml: * docs/plugins/inspect/plugin-vorbis.xml: * docs/plugins/inspect/plugin-ximagesink.xml: * docs/plugins/inspect/plugin-xvimagesink.xml: * ext/alsa/gstalsamixer.c: * ext/alsa/gstalsasink.c: * ext/alsa/gstalsasrc.c: * ext/gio/gstgiosink.c: * ext/gio/gstgiosrc.c: * ext/gio/gstgiostreamsink.c: * ext/gio/gstgiostreamsrc.c: * ext/gnomevfs/gstgnomevfssink.c: * ext/gnomevfs/gstgnomevfssrc.c: * ext/ogg/gstoggdemux.c: * ext/ogg/gstoggmux.c: * ext/pango/gstclockoverlay.c: * ext/pango/gsttextoverlay.c: * ext/pango/gsttextrender.c: * ext/pango/gsttimeoverlay.c: * ext/theora/theoradec.c: * ext/theora/theoraenc.c: * ext/theora/theoraparse.c: * ext/vorbis/vorbisdec.c: * ext/vorbis/vorbisenc.c: * ext/vorbis/vorbisparse.c: * ext/vorbis/vorbistag.c: * gst/adder/gstadder.c: * gst/audioconvert/gstaudioconvert.c: * gst/audioresample/gstaudioresample.c: * gst/audiotestsrc/gstaudiotestsrc.c: * gst/ffmpegcolorspace/gstffmpegcolorspace.c: * gst/gdp/gstgdpdepay.c: * gst/gdp/gstgdppay.c: * gst/playback/gstdecodebin2.c: * gst/playback/gstplaybin.c: * gst/playback/gstplaybin2.c: * gst/playback/gstqueue2.c: * gst/playback/gsturidecodebin.c: * gst/tcp/gstmultifdsink.c: * gst/tcp/gsttcpserversink.c: * gst/videorate/gstvideorate.c: * gst/videoscale/gstvideoscale.c: * gst/videotestsrc/gstvideotestsrc.c: * gst/volume/gstvolume.c: * sys/ximage/ximagesink.c: * sys/xvimage/xvimagesink.c: Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipelines are "simple" pipelines.
2008-07-10 21:06:06 +00:00
* ]|
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gsttextrender.h"
#include <string.h>
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
# define CAIRO_ARGB_A 3
# define CAIRO_ARGB_R 2
# define CAIRO_ARGB_G 1
# define CAIRO_ARGB_B 0
#else
# define CAIRO_ARGB_A 0
# define CAIRO_ARGB_R 1
# define CAIRO_ARGB_G 2
# define CAIRO_ARGB_B 3
#endif
GST_DEBUG_CATEGORY_EXTERN (pango_debug);
#define GST_CAT_DEFAULT pango_debug
#define MINIMUM_OUTLINE_OFFSET 1.0
#define DEFAULT_PROP_VALIGNMENT GST_TEXT_RENDER_VALIGN_BASELINE
#define DEFAULT_PROP_HALIGNMENT GST_TEXT_RENDER_HALIGN_CENTER
#define DEFAULT_PROP_LINE_ALIGNMENT GST_TEXT_RENDER_LINE_ALIGN_CENTER
#define DEFAULT_PROP_XPAD 25
#define DEFAULT_PROP_YPAD 25
#define DEFAULT_RENDER_WIDTH 720
#define DEFAULT_RENDER_HEIGHT 576
enum
{
PROP_0,
PROP_HALIGNMENT,
PROP_VALIGNMENT,
PROP_LINE_ALIGNMENT,
PROP_XPAD,
PROP_YPAD,
PROP_FONT_DESC
};
2011-06-16 10:48:33 +00:00
#define VIDEO_FORMATS "{ AYUV, ARGB } "
static GstStaticPadTemplate src_template_factory =
2011-06-16 10:48:33 +00:00
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
2011-06-16 10:48:33 +00:00
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
);
static GstStaticPadTemplate sink_template_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("text/x-raw, format = { pango-markup, utf8 }")
);
#define GST_TYPE_TEXT_RENDER_VALIGN (gst_text_render_valign_get_type())
static GType
gst_text_render_valign_get_type (void)
{
static GType text_render_valign_type = 0;
static const GEnumValue text_render_valign[] = {
{GST_TEXT_RENDER_VALIGN_BASELINE, "baseline", "baseline"},
{GST_TEXT_RENDER_VALIGN_BOTTOM, "bottom", "bottom"},
{GST_TEXT_RENDER_VALIGN_TOP, "top", "top"},
{0, NULL, NULL},
};
if (!text_render_valign_type) {
text_render_valign_type =
g_enum_register_static ("GstTextRenderVAlign", text_render_valign);
}
return text_render_valign_type;
}
#define GST_TYPE_TEXT_RENDER_HALIGN (gst_text_render_halign_get_type())
static GType
gst_text_render_halign_get_type (void)
{
static GType text_render_halign_type = 0;
static const GEnumValue text_render_halign[] = {
{GST_TEXT_RENDER_HALIGN_LEFT, "left", "left"},
{GST_TEXT_RENDER_HALIGN_CENTER, "center", "center"},
{GST_TEXT_RENDER_HALIGN_RIGHT, "right", "right"},
{0, NULL, NULL},
};
if (!text_render_halign_type) {
text_render_halign_type =
g_enum_register_static ("GstTextRenderHAlign", text_render_halign);
}
return text_render_halign_type;
}
#define GST_TYPE_TEXT_RENDER_LINE_ALIGN (gst_text_render_line_align_get_type())
static GType
gst_text_render_line_align_get_type (void)
{
static GType text_render_line_align_type = 0;
static const GEnumValue text_render_line_align[] = {
{GST_TEXT_RENDER_LINE_ALIGN_LEFT, "left", "left"},
{GST_TEXT_RENDER_LINE_ALIGN_CENTER, "center", "center"},
{GST_TEXT_RENDER_LINE_ALIGN_RIGHT, "right", "right"},
{0, NULL, NULL}
};
if (!text_render_line_align_type) {
text_render_line_align_type =
g_enum_register_static ("GstTextRenderLineAlign",
text_render_line_align);
}
return text_render_line_align_type;
}
static void gst_text_render_adjust_values_with_fontdesc (GstTextRender *
render, PangoFontDescription * desc);
#define gst_text_render_parent_class parent_class
G_DEFINE_TYPE (GstTextRender, gst_text_render, 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_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void
gst_text_render_class_init (GstTextRenderClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
PangoFontMap *fontmap;
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;
gobject_class->get_property = gst_text_render_get_property;
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&src_template_factory));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&sink_template_factory));
gst_element_class_set_static_metadata (gstelement_class, "Text renderer",
"Filter/Editor/Video",
"Renders a text string to an image bitmap",
"David Schleef <ds@schleef.org>, "
"GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
fontmap = pango_cairo_font_map_get_default ();
klass->pango_context =
pango_font_map_create_context (PANGO_FONT_MAP (fontmap));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_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"
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
" for syntax.", "", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGNMENT,
g_param_spec_enum ("valignment", "vertical alignment",
"Vertical alignment of the text", GST_TYPE_TEXT_RENDER_VALIGN,
DEFAULT_PROP_VALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGNMENT,
g_param_spec_enum ("halignment", "horizontal alignment",
"Horizontal alignment of the text", GST_TYPE_TEXT_RENDER_HALIGN,
DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
g_param_spec_int ("xpad", "horizontal paddding",
"Horizontal paddding when using left/right alignment", 0, G_MAXINT,
DEFAULT_PROP_XPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPAD,
g_param_spec_int ("ypad", "vertical padding",
"Vertical padding when using top/bottom alignment", 0, G_MAXINT,
DEFAULT_PROP_YPAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
g_param_spec_enum ("line-alignment", "line alignment",
"Alignment of text lines relative to each other.",
GST_TYPE_TEXT_RENDER_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
gst_text_render_adjust_values_with_fontdesc (GstTextRender * render,
PangoFontDescription * desc)
{
gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
render->shadow_offset = (double) (font_size) / 13.0;
render->outline_offset = (double) (font_size) / 15.0;
if (render->outline_offset < MINIMUM_OUTLINE_OFFSET)
render->outline_offset = MINIMUM_OUTLINE_OFFSET;
}
static void
gst_text_render_render_pangocairo (GstTextRender * render)
{
cairo_t *cr;
cairo_surface_t *surface;
cairo_t *cr_shadow;
cairo_surface_t *surface_shadow;
PangoRectangle ink_rect, logical_rect;
gint width, height;
pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect);
width = logical_rect.width + render->shadow_offset;
height = logical_rect.height + logical_rect.y + render->shadow_offset;
surface_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
cr_shadow = cairo_create (surface_shadow);
/* clear shadow surface */
cairo_set_operator (cr_shadow, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr_shadow);
cairo_set_operator (cr_shadow, CAIRO_OPERATOR_OVER);
/* draw shadow text */
cairo_save (cr_shadow);
cairo_set_source_rgba (cr_shadow, 0.0, 0.0, 0.0, 0.5);
cairo_translate (cr_shadow, render->shadow_offset, render->shadow_offset);
pango_cairo_show_layout (cr_shadow, render->layout);
cairo_restore (cr_shadow);
/* draw outline text */
cairo_save (cr_shadow);
cairo_set_source_rgb (cr_shadow, 0.0, 0.0, 0.0);
cairo_set_line_width (cr_shadow, render->outline_offset);
pango_cairo_layout_path (cr_shadow, render->layout);
cairo_stroke (cr_shadow);
cairo_restore (cr_shadow);
cairo_destroy (cr_shadow);
render->text_image = g_realloc (render->text_image, 4 * width * height);
surface = cairo_image_surface_create_for_data (render->text_image,
CAIRO_FORMAT_ARGB32, width, height, width * 4);
cr = cairo_create (surface);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
/* set default color */
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
cairo_save (cr);
/* draw text */
pango_cairo_show_layout (cr, render->layout);
cairo_restore (cr);
/* composite shadow with offset */
cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
cairo_set_source_surface (cr, surface_shadow, 0.0, 0.0);
cairo_paint (cr);
cairo_destroy (cr);
cairo_surface_destroy (surface_shadow);
cairo_surface_destroy (surface);
render->image_width = width;
render->image_height = height;
}
static void
gst_text_render_check_argb (GstTextRender * render)
{
GstCaps *peer_caps;
peer_caps = gst_pad_get_allowed_caps (render->srcpad);
if (G_LIKELY (peer_caps)) {
guint i = 0, n = 0;
n = gst_caps_get_size (peer_caps);
GST_DEBUG_OBJECT (render, "peer allowed caps (%u structure(s)) are %"
GST_PTR_FORMAT, n, peer_caps);
/* Check if AYUV or ARGB is first */
for (i = 0; i < n; i++) {
2011-06-16 10:48:33 +00:00
GstStructure *s;
GstVideoFormat vformat;
const GstVideoFormatInfo *info;
2011-06-16 10:48:33 +00:00
const gchar *fmt;
s = gst_caps_get_structure (peer_caps, i);
if (!gst_structure_has_name (s, "video/x-raw"))
continue;
fmt = gst_structure_get_string (s, "format");
if (fmt == NULL)
continue;
vformat = gst_video_format_from_string (fmt);
info = gst_video_format_get_info (vformat);
if (info == NULL)
continue;
render->use_ARGB = GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info);
}
gst_caps_unref (peer_caps);
}
}
static gboolean
gst_text_render_src_setcaps (GstTextRender * render, GstCaps * caps)
{
GstStructure *structure;
gboolean ret;
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_OBJECT (render, "Got caps %" GST_PTR_FORMAT, caps);
if (width >= render->image_width && height >= render->image_height) {
render->width = width;
render->height = height;
}
gst_text_render_check_argb (render);
ret = gst_pad_set_caps (render->srcpad, caps);
return ret;
}
2012-03-11 18:04:41 +00:00
static GstCaps *
2011-11-10 10:02:12 +00:00
gst_text_render_fixate_caps (GstTextRender * render, GstCaps * caps)
{
2011-11-10 10:02:12 +00:00
GstStructure *s;
2012-03-11 18:04:41 +00:00
caps = gst_caps_truncate (caps);
caps = gst_caps_make_writable (caps);
2011-11-10 10:02:12 +00:00
s = gst_caps_get_structure (caps, 0);
GST_DEBUG ("Fixating caps %" GST_PTR_FORMAT, caps);
gst_structure_fixate_field_nearest_int (s, "width", MAX (render->image_width,
DEFAULT_RENDER_WIDTH));
gst_structure_fixate_field_nearest_int (s, "height",
MAX (render->image_height + render->ypad, DEFAULT_RENDER_HEIGHT));
caps = gst_caps_fixate (caps);
GST_DEBUG ("Fixated to %" GST_PTR_FORMAT, caps);
2012-03-11 18:04:41 +00:00
return caps;
}
#define CAIRO_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
} G_STMT_END
static void
gst_text_renderer_image_to_ayuv (GstTextRender * render, guchar * pixbuf,
int xpos, int ypos, int stride)
{
int y; /* text bitmap coordinates */
guchar *p, *bitp;
guchar a, r, g, b;
int width, height;
width = render->image_width;
height = render->image_height;
for (y = 0; y < height && ypos + y < render->height; y++) {
int n;
p = pixbuf + (ypos + y) * stride + xpos * 4;
bitp = render->text_image + y * width * 4;
for (n = 0; n < width && n < render->width; n++) {
b = bitp[CAIRO_ARGB_B];
g = bitp[CAIRO_ARGB_G];
r = bitp[CAIRO_ARGB_R];
a = bitp[CAIRO_ARGB_A];
bitp += 4;
/* Cairo uses pre-multiplied ARGB, unpremultiply it */
CAIRO_UNPREMULTIPLY (a, r, g, b);
*p++ = a;
*p++ = CLAMP ((int) (((19595 * r) >> 16) + ((38470 * g) >> 16) +
((7471 * b) >> 16)), 0, 255);
*p++ = CLAMP ((int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) +
((32768 * b) >> 16) + 128), 0, 255);
*p++ = CLAMP ((int) (((32768 * r) >> 16) - ((27439 * g) >> 16) -
((5329 * b) >> 16) + 128), 0, 255);
}
}
}
static void
gst_text_renderer_image_to_argb (GstTextRender * render, guchar * pixbuf,
int xpos, int ypos, int stride)
{
int i, j;
guchar *p, *bitp;
int width, height;
width = render->image_width;
height = render->image_height;
for (i = 0; i < height && ypos + i < render->height; i++) {
p = pixbuf + (ypos + i) * stride + xpos * 4;
bitp = render->text_image + i * width * 4;
for (j = 0; j < width && j < render->width; j++) {
p[0] = bitp[CAIRO_ARGB_A];
p[1] = bitp[CAIRO_ARGB_R];
p[2] = bitp[CAIRO_ARGB_G];
p[3] = bitp[CAIRO_ARGB_B];
/* Cairo uses pre-multiplied ARGB, unpremultiply it */
CAIRO_UNPREMULTIPLY (p[0], p[1], p[2], p[3]);
bitp += 4;
p += 4;
}
}
}
static GstFlowReturn
2011-11-17 11:48:25 +00:00
gst_text_render_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf)
{
GstTextRender *render;
GstFlowReturn ret;
GstBuffer *outbuf;
2011-05-26 11:47:24 +00:00
GstCaps *caps = NULL, *padcaps;
2012-01-20 15:11:54 +00:00
GstMapInfo map;
guint8 *data;
gsize size;
gint n;
gint xpos, ypos;
2011-11-17 11:48:25 +00:00
render = GST_TEXT_RENDER (parent);
2012-01-20 15:11:54 +00:00
gst_buffer_map (inbuf, &map, GST_MAP_READ);
data = map.data;
size = map.size;
/* 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'", (gint) size, data);
pango_layout_set_markup (render->layout, (gchar *) data, size);
gst_text_render_render_pangocairo (render);
2012-01-20 15:11:54 +00:00
gst_buffer_unmap (inbuf, &map);
gst_text_render_check_argb (render);
padcaps = gst_pad_query_caps (render->srcpad, NULL);
2011-11-15 16:17:53 +00:00
caps = gst_pad_peer_query_caps (render->srcpad, padcaps);
gst_caps_unref (padcaps);
if (!caps || gst_caps_is_empty (caps)) {
GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL));
ret = GST_FLOW_ERROR;
goto done;
}
2012-03-11 18:04:41 +00:00
caps = gst_text_render_fixate_caps (render, caps);
if (!gst_text_render_src_setcaps (render, caps)) {
GST_ELEMENT_ERROR (render, CORE, NEGOTIATION, (NULL), (NULL));
ret = GST_FLOW_ERROR;
goto done;
}
if (render->segment_event) {
gst_pad_push_event (render->srcpad, render->segment_event);
render->segment_event = NULL;
}
GST_DEBUG ("Allocating buffer WxH = %dx%d", render->width, render->height);
2011-04-29 11:28:17 +00:00
outbuf = gst_buffer_new_and_alloc (render->width * render->height * 4);
gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
2012-01-20 15:11:54 +00:00
gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
data = map.data;
size = map.size;
if (render->use_ARGB) {
memset (data, 0, render->width * render->height * 4);
} else {
for (n = 0; n < render->width * render->height; n++) {
data[n * 4] = data[n * 4 + 1] = 0;
data[n * 4 + 2] = data[n * 4 + 3] = 128;
}
}
switch (render->halign) {
case GST_TEXT_RENDER_HALIGN_LEFT:
xpos = render->xpad;
break;
case GST_TEXT_RENDER_HALIGN_CENTER:
xpos = (render->width - render->image_width) / 2;
break;
case GST_TEXT_RENDER_HALIGN_RIGHT:
xpos = render->width - render->image_width - render->xpad;
break;
default:
xpos = 0;
}
switch (render->valign) {
case GST_TEXT_RENDER_VALIGN_BOTTOM:
ypos = render->height - render->image_height - render->ypad;
break;
case GST_TEXT_RENDER_VALIGN_BASELINE:
ypos = render->height - (render->image_height + render->ypad);
break;
case GST_TEXT_RENDER_VALIGN_TOP:
ypos = render->ypad;
break;
default:
ypos = render->ypad;
break;
}
if (render->text_image) {
if (render->use_ARGB) {
gst_text_renderer_image_to_argb (render, data, xpos, ypos,
render->width * 4);
} else {
gst_text_renderer_image_to_ayuv (render, data, xpos, ypos,
render->width * 4);
}
}
2012-01-20 15:11:54 +00:00
gst_buffer_unmap (outbuf, &map);
ret = gst_pad_push (render->srcpad, outbuf);
done:
if (caps)
gst_caps_unref (caps);
gst_buffer_unref (inbuf);
2011-11-17 11:48:25 +00:00
return ret;
}
static gboolean
gst_text_render_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstTextRender *render = GST_TEXT_RENDER (parent);
gboolean ret = TRUE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEGMENT:
{
if (gst_pad_has_current_caps (render->srcpad)) {
ret = gst_pad_push_event (render->srcpad, event);
} else {
gst_event_replace (&render->segment_event, event);
gst_event_unref (event);
}
break;
}
default:
ret = gst_pad_push_event (render->srcpad, event);
break;
}
return ret;
}
static void
gst_text_render_finalize (GObject * object)
{
GstTextRender *render = GST_TEXT_RENDER (object);
gst_event_replace (&render->segment_event, NULL);
g_free (render->text_image);
if (render->layout)
g_object_unref (render->layout);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_text_render_init (GstTextRender * render)
{
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_pad_set_event_function (render->sinkpad,
GST_DEBUG_FUNCPTR (gst_text_render_event));
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_element_add_pad (GST_ELEMENT (render), render->srcpad);
render->line_align = DEFAULT_PROP_LINE_ALIGNMENT;
render->layout =
pango_layout_new (GST_TEXT_RENDER_GET_CLASS (render)->pango_context);
pango_layout_set_alignment (render->layout,
(PangoAlignment) render->line_align);
render->halign = DEFAULT_PROP_HALIGNMENT;
render->valign = DEFAULT_PROP_VALIGNMENT;
render->xpad = DEFAULT_PROP_XPAD;
render->ypad = DEFAULT_PROP_YPAD;
render->width = DEFAULT_RENDER_WIDTH;
render->height = DEFAULT_RENDER_HEIGHT;
render->use_ARGB = FALSE;
}
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 PROP_VALIGNMENT:
render->valign = g_value_get_enum (value);
break;
case PROP_HALIGNMENT:
render->halign = g_value_get_enum (value);
break;
case PROP_LINE_ALIGNMENT:
render->line_align = g_value_get_enum (value);
pango_layout_set_alignment (render->layout,
(PangoAlignment) render->line_align);
break;
case PROP_XPAD:
render->xpad = g_value_get_int (value);
break;
case PROP_YPAD:
render->ypad = g_value_get_int (value);
break;
case PROP_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);
gst_text_render_adjust_values_with_fontdesc (render, desc);
pango_font_description_free (desc);
gst_text_render_render_pangocairo (render);
GST_OBJECT_UNLOCK (render);
} else {
GST_WARNING ("font description parse failed: %s",
g_value_get_string (value));
}
break;
}
default:
break;
}
}
static void
gst_text_render_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstTextRender *render = GST_TEXT_RENDER (object);
switch (prop_id) {
case PROP_VALIGNMENT:
g_value_set_enum (value, render->valign);
break;
case PROP_HALIGNMENT:
g_value_set_enum (value, render->halign);
break;
case PROP_LINE_ALIGNMENT:
g_value_set_enum (value, render->line_align);
break;
case PROP_XPAD:
g_value_set_int (value, render->xpad);
break;
case PROP_YPAD:
g_value_set_int (value, render->ypad);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}