From 0163a876c004fc0edd8ca64e9cf5cb72f854b274 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Thu, 16 Dec 2004 05:32:07 +0000 Subject: configure.ac: add audioresample and cairo plugins. Remove Original commit message from CVS: * configure.ac: add audioresample and cairo plugins. Remove HAVE_MMX stuff, because it's not used. * ext/Makefile.am: same * ext/audioresample/Makefile.am: You are not ready for an audio resampling element based on audioresample. * ext/audioresample/gstaudioresample.c: * ext/audioresample/gstaudioresample.h: * ext/cairo/Makefile.am: You are not ready for overlay elements based on cairo. Don't look too closely, these elements kinda suck right now. * ext/cairo/gstcairo.c: new * ext/cairo/gsttextoverlay.c: new * ext/cairo/gsttextoverlay.h: new * ext/cairo/gsttimeoverlay.c: new * ext/cairo/gsttimeoverlay.h: new * gst-libs/gst/media-info/media-info-priv.h: fix compile problem with compilers that don't support variadic macros. --- ChangeLog | 20 ++ configure.ac | 33 +-- ext/Makefile.am | 16 ++ ext/cairo/Makefile.am | 14 ++ ext/cairo/gstcairo.c | 46 ++++ ext/cairo/gsttextoverlay.c | 614 +++++++++++++++++++++++++++++++++++++++++++++ ext/cairo/gsttextoverlay.h | 75 ++++++ ext/cairo/gsttimeoverlay.c | 306 ++++++++++++++++++++++ ext/cairo/gsttimeoverlay.h | 64 +++++ 9 files changed, 1173 insertions(+), 15 deletions(-) create mode 100644 ext/cairo/Makefile.am create mode 100644 ext/cairo/gstcairo.c create mode 100644 ext/cairo/gsttextoverlay.c create mode 100644 ext/cairo/gsttextoverlay.h create mode 100644 ext/cairo/gsttimeoverlay.c create mode 100644 ext/cairo/gsttimeoverlay.h diff --git a/ChangeLog b/ChangeLog index b301bcaf..cf7a5a30 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2004-12-15 David Schleef + + * configure.ac: add audioresample and cairo plugins. Remove + HAVE_MMX stuff, because it's not used. + * ext/Makefile.am: same + * ext/audioresample/Makefile.am: You are not ready for an + audio resampling element based on audioresample. + * ext/audioresample/gstaudioresample.c: + * ext/audioresample/gstaudioresample.h: + * ext/cairo/Makefile.am: You are not ready for overlay elements + based on cairo. Don't look too closely, these elements kinda + suck right now. + * ext/cairo/gstcairo.c: new + * ext/cairo/gsttextoverlay.c: new + * ext/cairo/gsttextoverlay.h: new + * ext/cairo/gsttimeoverlay.c: new + * ext/cairo/gsttimeoverlay.h: new + * gst-libs/gst/media-info/media-info-priv.h: fix compile + problem with compilers that don't support variadic macros. + 2004-12-15 Balamurali Viswanathan Reviewed by: David Schleef diff --git a/configure.ac b/configure.ac index 72514037..e64bea79 100644 --- a/configure.ac +++ b/configure.ac @@ -806,6 +806,22 @@ GST_CHECK_FEATURE(AUDIOFILE, [audiofile], afsink afsrc, [ AC_CHECK_LIB(audiofile, af_virtual_file_new, , HAVE_AUDIOFILE="no") fi]) +dnl *** audioresample *** +translit(dnm, m, l) AM_CONDITIONAL(USE_AUDIORESAMPLE, true) +GST_CHECK_FEATURE(AUDIORESAMPLE, [audioresample plug-in], audioresample, [ + PKG_CHECK_MODULES(AUDIORESAMPLE, audioresample-0.1, HAVE_AUDIORESAMPLE=yes, HAVE_AUDIORESAMPLE=no) + AC_SUBST(AUDIORESAMPLE_CFLAGS) + AC_SUBST(AUDIORESAMPLE_LIBS) +]) + +dnl *** cairo *** +translit(dnm, m, l) AM_CONDITIONAL(USE_CAIRO, true) +GST_CHECK_FEATURE(CAIRO, [cairo plug-in], cairo, [ + PKG_CHECK_MODULES(CAIRO, cairo, HAVE_CAIRO=yes, HAVE_CAIRO=no) + AC_SUBST(CAIRO_CFLAGS) + AC_SUBST(CAIRO_LIBS) +]) + dnl *** cdaudio *** translit(dnm, m, l) AM_CONDITIONAL(USE_CDAUDIO, true) GST_CHECK_FEATURE(CDAUDIO, [cdaudio], cdaudio, [ @@ -1722,15 +1738,6 @@ dnl ###################################################################### dnl # Check command line parameters, and set shell variables accordingly # dnl ###################################################################### -AC_ARG_ENABLE(libmmx, - AC_HELP_STRING([--enable-libmmx],[use libmmx, if available]), -[case "${enableval}" in - yes) USE_LIBMMX=$HAVE_LIBMMX ;; - no) USE_LIBMMX=no ;; - *) AC_MSG_ERROR(bad value ${enableval} for --enable-libmmx) ;; -esac], -[USE_LIBMMX=$HAVE_LIBMMX]) dnl Default value - AC_ARG_ENABLE(atomic, AC_HELP_STRING([--enable-atomic],[use atomic reference counting header]), [case "${enableval}" in @@ -1783,10 +1790,6 @@ dnl # Set defines according to variables set above # dnl ################################################ -if test "x$USE_LIBMMX" = xyes; then - AC_DEFINE(HAVE_LIBMMX, 1, [Define if libmmx is available]) -fi - if test "x$USE_ATOMIC_H" = xyes; then AC_DEFINE(HAVE_ATOMIC_H, 1, [Define if atomic.h header file is available]) fi @@ -1804,8 +1807,6 @@ dnl ############################# dnl These should be "USE_*" instead of "HAVE_*", but some packages expect dnl HAVE_ and it is likely to be easier to stick with the old name -AM_CONDITIONAL(HAVE_LIBMMX, test "x$USE_LIBMMX" = "xyes") - AM_CONDITIONAL(HAVE_ATOMIC_H, test "x$USE_ATOMIC_H" = "xyes") AM_CONDITIONAL(EXPERIMENTAL, test "$EXPERIMENTAL" = "$xyes") @@ -1980,6 +1981,8 @@ ext/alsa/Makefile ext/arts/Makefile ext/artsd/Makefile ext/audiofile/Makefile +ext/audioresample/Makefile +ext/cairo/Makefile ext/cdaudio/Makefile ext/cdparanoia/Makefile ext/dirac/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 42faa62c..934e0a2c 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -34,6 +34,18 @@ else AUDIOFILE_DIR= endif +if USE_AUDIORESAMPLE +AUDIORESAMPLE_DIR=audioresample +else +AUDIORESAMPLE_DIR= +endif + +if USE_CAIRO +CAIRO_DIR=cairo +else +CAIRO_DIR= +endif + if USE_CDAUDIO CDAUDIO_DIR=cdaudio else @@ -383,6 +395,8 @@ SUBDIRS=\ $(ARTS_DIR) \ $(ARTSC_DIR) \ $(AUDIOFILE_DIR) \ + $(AUDIORESAMPLE_DIR) \ + $(CAIRO_DIR) \ $(CDAUDIO_DIR) \ $(CDPARANOIA_DIR) \ $(DIRAC_DIR) \ @@ -446,6 +460,8 @@ DIST_SUBDIRS=\ arts \ artsd \ audiofile \ + audioresample \ + cairo \ cdaudio \ cdparanoia \ dirac \ diff --git a/ext/cairo/Makefile.am b/ext/cairo/Makefile.am new file mode 100644 index 00000000..8e80b626 --- /dev/null +++ b/ext/cairo/Makefile.am @@ -0,0 +1,14 @@ + +plugin_LTLIBRARIES = libgstcairo.la + +noinst_HEADERS = gsttimeoverlay.h gsttextoverlay.h + +libgstcairo_la_SOURCES = \ + gsttimeoverlay.c \ + gsttextoverlay.c \ + gstcairo.c +libgstcairo_la_CFLAGS = $(GST_CFLAGS) $(CAIRO_CFLAGS) \ + -I$(top_srcdir)/gst/videofilter +libgstcairo_la_LIBADD = +libgstcairo_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(CAIRO_LIBS) -lm + diff --git a/ext/cairo/gstcairo.c b/ext/cairo/gstcairo.c new file mode 100644 index 00000000..9d6a5f23 --- /dev/null +++ b/ext/cairo/gstcairo.c @@ -0,0 +1,46 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2003,2004> David Schleef + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/*#define DEBUG_ENABLED */ +#include +#include +#include +#include + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_library_load ("gstvideofilter")) + return FALSE; + + gst_element_register (plugin, "cairotextoverlay", GST_RANK_NONE, + GST_TYPE_TEXTOVERLAY); + gst_element_register (plugin, "cairotimeoverlay", GST_RANK_NONE, + GST_TYPE_TIMEOVERLAY); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "timeoverlay", + "Time overlay", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN) diff --git a/ext/cairo/gsttextoverlay.c b/ext/cairo/gsttextoverlay.c new file mode 100644 index 00000000..4ca302ee --- /dev/null +++ b/ext/cairo/gsttextoverlay.c @@ -0,0 +1,614 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2003> David Schleef + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include "gsttextoverlay.h" + +static GstElementDetails textoverlay_details = { + "Text Overlay", + "Filter/Editor/Video", + "Adds text strings on top of a video buffer", + "David Schleef " +}; + +enum +{ + ARG_0, + ARG_TEXT, + ARG_VALIGN, + ARG_HALIGN, + ARG_X0, + ARG_Y0, + ARG_FONT_DESC +}; + + +static GstStaticPadTemplate textoverlay_src_template_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv, " + "format = (fourcc) I420, " + "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") + ); + +static GstStaticPadTemplate video_sink_template_factory = +GST_STATIC_PAD_TEMPLATE ("video_sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-yuv, " + "format = (fourcc) I420, " + "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") + ); + +static GstStaticPadTemplate text_sink_template_factory = +GST_STATIC_PAD_TEMPLATE ("text_sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("text/plain") + ); + +static void gst_textoverlay_base_init (gpointer g_class); +static void gst_textoverlay_class_init (GstTextOverlayClass * klass); +static void gst_textoverlay_init (GstTextOverlay * overlay); +static void gst_textoverlay_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_textoverlay_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static GstElementStateReturn gst_textoverlay_change_state (GstElement * + element); +static void gst_textoverlay_finalize (GObject * object); + + +static GstElementClass *parent_class = NULL; + +/*static guint gst_textoverlay_signals[LAST_SIGNAL] = { 0 }; */ + + +GType +gst_textoverlay_get_type (void) +{ + static GType textoverlay_type = 0; + + if (!textoverlay_type) { + static const GTypeInfo textoverlay_info = { + sizeof (GstTextOverlayClass), + gst_textoverlay_base_init, + NULL, + (GClassInitFunc) gst_textoverlay_class_init, + NULL, + NULL, + sizeof (GstTextOverlay), + 0, + (GInstanceInitFunc) gst_textoverlay_init, + }; + + textoverlay_type = + g_type_register_static (GST_TYPE_ELEMENT, "GstTextOverlay", + &textoverlay_info, 0); + } + return textoverlay_type; +} + +static void +gst_textoverlay_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 (&textoverlay_src_template_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&video_sink_template_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&text_sink_template_factory)); + + gst_element_class_set_details (element_class, &textoverlay_details); +} + +static void +gst_textoverlay_class_init (GstTextOverlayClass * 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_textoverlay_finalize; + gobject_class->set_property = gst_textoverlay_set_property; + gobject_class->get_property = gst_textoverlay_get_property; + + gstelement_class->change_state = gst_textoverlay_change_state; + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TEXT, + g_param_spec_string ("text", "text", + "Text to be display.", "", G_PARAM_WRITABLE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_VALIGN, + g_param_spec_string ("valign", "vertical alignment", + "Vertical alignment of the text. " + "Can be either 'baseline', 'bottom', or 'top'", + "baseline", G_PARAM_WRITABLE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HALIGN, + g_param_spec_string ("halign", "horizontal alignment", + "Horizontal alignment of the text. " + "Can be either 'left', 'right', or 'center'", + "center", G_PARAM_WRITABLE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_X0, + g_param_spec_int ("x0", "X position", + "Initial X position." + " Horizontal aligment takes this point" + " as reference.", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_Y0, + g_param_spec_int ("y0", "Y position", + "Initial Y position." + " Vertical aligment takes this point" + " as reference.", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE)); + 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)); +} + + +#if 0 +static void +resize_bitmap (GstTextOverlay * overlay, int width, int height) +{ + FT_Bitmap *bitmap = &overlay->bitmap; + int pitch = (width | 3) + 1; + int size = pitch * height; + + /* no need to keep reallocating; just keep the maximum size so far */ + if (size <= overlay->bitmap_buffer_size) { + bitmap->rows = height; + bitmap->width = width; + bitmap->pitch = pitch; + memset (bitmap->buffer, 0, overlay->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); + overlay->bitmap_buffer_size = size; +} +#endif + +static void +gst_textoverlay_render_text (GstTextOverlay * overlay, gchar * text, + int textlen) +{ + cairo_text_extents_t extents; + char *string; + + string = g_strdup_printf ("%.*s", textlen, text); + + if (overlay->pixbuf) + g_free (overlay->pixbuf); + overlay->pixbuf = g_malloc (4 * overlay->width * overlay->text_height); + cairo_set_target_image (overlay->cr, overlay->pixbuf, CAIRO_FORMAT_ARGB32, + overlay->width, overlay->text_height, overlay->width * 4); + + cairo_save (overlay->cr); + cairo_rectangle (overlay->cr, 0, 0, overlay->width, overlay->text_height); + cairo_set_alpha (overlay->cr, 0.0); + cairo_set_operator (overlay->cr, CAIRO_OPERATOR_SRC); + cairo_fill (overlay->cr); + cairo_restore (overlay->cr); + + cairo_save (overlay->cr); + cairo_text_extents (overlay->cr, string, &extents); + cairo_set_rgb_color (overlay->cr, 1, 1, 1); + cairo_move_to (overlay->cr, 0, overlay->text_height - 2); + cairo_show_text (overlay->cr, string); + + cairo_text_path (overlay->cr, string); + cairo_set_rgb_color (overlay->cr, 0, 0, 0); + cairo_set_line_width (overlay->cr, 1.0); + cairo_stroke (overlay->cr); + + g_free (string); + + cairo_restore (overlay->cr); + + overlay->text_width = MIN (extents.width, overlay->width); +} + +/* static GstPadLinkReturn */ +/* gst_textoverlay_text_sinkconnect (GstPad *pad, GstCaps *caps) */ +/* { */ +/* return GST_PAD_LINK_DONE; */ +/* } */ + + +static GstPadLinkReturn +gst_textoverlay_video_sinkconnect (GstPad * pad, const GstCaps * caps) +{ + GstTextOverlay *overlay; + GstStructure *structure; + + overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad)); + + structure = gst_caps_get_structure (caps, 0); + overlay->width = overlay->height = 0; + gst_structure_get_int (structure, "width", &overlay->width); + gst_structure_get_int (structure, "height", &overlay->height); + + return gst_pad_try_set_caps (overlay->srcpad, caps); +} + + +static void +gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, guchar * pixbuf, + int x0, int y0) +{ + int i; + int j; + int x, a, y; + + if (x0 < 0) + x0 = 0; + if (y0 < 0) + y0 = 0; + + GST_LOG ("%d %d %d %d %d %d %p", overlay->text_width, overlay->text_height, + overlay->width, overlay->height, x0, y0, pixbuf); + + for (i = 0; i < overlay->text_height; i++) { + for (j = 0; j < overlay->text_width; j++) { +#if 0 + x = pixbuf[(i + y0) * overlay->width + (j + x0)]; + a = overlay->pixbuf[4 * (i * overlay->width + j) + 0]; + y = overlay->pixbuf[4 * (i * overlay->width + j) + 1]; + pixbuf[(i + y0) * overlay->width + (j + x0)] = + (y * a + x * (255 - a)) / 255; +#endif + x = a = 0; + y = overlay->pixbuf[4 * (i * overlay->width + j) + 1]; + pixbuf[(i + y0) * overlay->width + (j + x0)] = y; + } + } + +} + + +static void +gst_textoverlay_video_chain (GstPad * pad, GstData * _data) +{ + GstBuffer *buf = GST_BUFFER (_data); + GstTextOverlay *overlay; + guchar *pixbuf; + gint x0, y0; + + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (buf != NULL); + overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad)); + g_return_if_fail (overlay != NULL); + g_return_if_fail (GST_IS_TEXTOVERLAY (overlay)); + + if (!GST_IS_BUFFER (_data)) + return; + + pixbuf = GST_BUFFER_DATA (buf); + + x0 = overlay->x0; + y0 = overlay->y0; + switch (overlay->valign) { + case GST_TEXT_OVERLAY_VALIGN_BOTTOM: + y0 -= overlay->text_height; + break; + case GST_TEXT_OVERLAY_VALIGN_BASELINE: +#define BASELINE 2 + y0 -= (overlay->text_height - BASELINE); + break; + case GST_TEXT_OVERLAY_VALIGN_TOP: + break; + } + + switch (overlay->halign) { + case GST_TEXT_OVERLAY_HALIGN_LEFT: + break; + case GST_TEXT_OVERLAY_HALIGN_RIGHT: + x0 -= overlay->text_width; + break; + case GST_TEXT_OVERLAY_HALIGN_CENTER: + x0 -= overlay->text_width; + break; + } + + gst_text_overlay_blit_yuv420 (overlay, pixbuf, x0, y0); + + gst_pad_push (overlay->srcpad, GST_DATA (buf)); +} + +#define PAST_END(buffer, time) \ + (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE && \ + GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE && \ + GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) \ + < (time)) + +static void +gst_textoverlay_loop (GstElement * element) +{ + GstTextOverlay *overlay; + GstBuffer *video_frame; + guint64 now; + + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_TEXTOVERLAY (element)); + overlay = GST_TEXTOVERLAY (element); + + video_frame = GST_BUFFER (gst_pad_pull (overlay->video_sinkpad)); + now = GST_BUFFER_TIMESTAMP (video_frame); + + /* + * This state machine has a bug that can't be resolved easily. + * (Needs a more complicated state machine.) Basically, if the + * text that came from a buffer from the sink pad is being + * displayed, and the default text is changed by set_parameter, + * we'll incorrectly display the default text. + * + * Otherwise, this is a pretty decent state machine that handles + * buffer timestamps and durations correctly. (I think) + */ + + while (overlay->next_buffer == NULL) { + GST_DEBUG ("attempting to pull a buffer"); + + /* read all text buffers until we get one "in the future" */ + if (!GST_PAD_IS_USABLE (overlay->text_sinkpad)) { + break; + } + overlay->next_buffer = GST_BUFFER (gst_pad_pull (overlay->text_sinkpad)); + if (!overlay->next_buffer) + break; + + if (PAST_END (overlay->next_buffer, now)) { + gst_buffer_unref (overlay->next_buffer); + overlay->next_buffer = NULL; + } + } + + if (overlay->next_buffer && + (GST_BUFFER_TIMESTAMP (overlay->next_buffer) <= now || + GST_BUFFER_TIMESTAMP (overlay->next_buffer) == GST_CLOCK_TIME_NONE)) { + GST_DEBUG ("using new buffer"); + + if (overlay->current_buffer) { + gst_buffer_unref (overlay->current_buffer); + } + overlay->current_buffer = overlay->next_buffer; + overlay->next_buffer = NULL; + + GST_DEBUG ("rendering '%*s'", + GST_BUFFER_SIZE (overlay->current_buffer), + GST_BUFFER_DATA (overlay->current_buffer)); + gst_textoverlay_render_text (overlay, + GST_BUFFER_DATA (overlay->current_buffer), + GST_BUFFER_SIZE (overlay->current_buffer)); + overlay->need_render = FALSE; + } + + if (overlay->current_buffer && PAST_END (overlay->current_buffer, now)) { + GST_DEBUG ("dropping old buffer"); + + gst_buffer_unref (overlay->current_buffer); + overlay->current_buffer = NULL; + + overlay->need_render = TRUE; + } + + if (overlay->need_render) { + GST_DEBUG ("rendering '%s'", overlay->default_text); + gst_textoverlay_render_text (overlay, + overlay->default_text, strlen (overlay->default_text)); + + overlay->need_render = FALSE; + } + + gst_textoverlay_video_chain (overlay->srcpad, GST_DATA (video_frame)); +} + + +static GstElementStateReturn +gst_textoverlay_change_state (GstElement * element) +{ + GstTextOverlay *overlay; + + overlay = GST_TEXTOVERLAY (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_PAUSED_TO_PLAYING: + break; + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + break; + } + + parent_class->change_state (element); + + return GST_STATE_SUCCESS; +} + +static void +gst_textoverlay_finalize (GObject * object) +{ + GstTextOverlay *overlay = GST_TEXTOVERLAY (object); + + if (overlay->cr) { + cairo_destroy (overlay->cr); + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_textoverlay_init (GstTextOverlay * overlay) +{ + cairo_font_extents_t font_extents; + + /* video sink */ + overlay->video_sinkpad = + gst_pad_new_from_template (gst_static_pad_template_get + (&video_sink_template_factory), "video_sink"); + gst_pad_set_link_function (overlay->video_sinkpad, + gst_textoverlay_video_sinkconnect); + gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad); + + /* text sink */ + overlay->text_sinkpad = + gst_pad_new_from_template (gst_static_pad_template_get + (&text_sink_template_factory), "text_sink"); + gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad); + + /* (video) source */ + overlay->srcpad = + gst_pad_new_from_template (gst_static_pad_template_get + (&textoverlay_src_template_factory), "src"); + gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad); + + overlay->cr = cairo_create (); + + overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER; + overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE; + overlay->x0 = overlay->y0 = 0; + + overlay->default_text = g_strdup (""); + overlay->need_render = TRUE; + + cairo_select_font (overlay->cr, "sans", 0, 0); + cairo_scale_font (overlay->cr, 50); + + cairo_current_font_extents (overlay->cr, &font_extents); + overlay->text_height = font_extents.height; + + gst_element_set_loop_function (GST_ELEMENT (overlay), gst_textoverlay_loop); +} + + +static void +gst_textoverlay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstTextOverlay *overlay; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_TEXTOVERLAY (object)); + overlay = GST_TEXTOVERLAY (object); + + switch (prop_id) { + + case ARG_TEXT: + if (overlay->default_text) { + g_free (overlay->default_text); + } + overlay->default_text = g_strdup (g_value_get_string (value)); + overlay->need_render = TRUE; + break; + + case ARG_VALIGN: + if (strcasecmp (g_value_get_string (value), "baseline") == 0) + overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE; + else if (strcasecmp (g_value_get_string (value), "bottom") == 0) + overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM; + else if (strcasecmp (g_value_get_string (value), "top") == 0) + overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP; + else + g_warning ("Invalid 'valign' property value: %s", + g_value_get_string (value)); + break; + + case ARG_HALIGN: + if (strcasecmp (g_value_get_string (value), "left") == 0) + overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT; + else if (strcasecmp (g_value_get_string (value), "right") == 0) + overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT; + else if (strcasecmp (g_value_get_string (value), "center") == 0) + overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER; + else + g_warning ("Invalid 'halign' property value: %s", + g_value_get_string (value)); + break; + + case ARG_X0: + overlay->x0 = g_value_get_int (value); + break; + + case ARG_Y0: + overlay->y0 = g_value_get_int (value); + break; + + case ARG_FONT_DESC: + { +#if 0 + PangoFontDescription *desc; + + desc = pango_font_description_from_string (g_value_get_string (value)); + if (desc) { + g_message ("font description set: %s", g_value_get_string (value)); + pango_layout_set_font_description (overlay->layout, desc); + pango_font_description_free (desc); + render_text (overlay); + } else + g_warning ("font description parse failed: %s", + g_value_get_string (value)); +#endif + break; + } + + default: + break; + } +} + +static void +gst_textoverlay_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstTextOverlay *overlay; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_TEXTOVERLAY (object)); + overlay = GST_TEXTOVERLAY (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} diff --git a/ext/cairo/gsttextoverlay.h b/ext/cairo/gsttextoverlay.h new file mode 100644 index 00000000..ff539131 --- /dev/null +++ b/ext/cairo/gsttextoverlay.h @@ -0,0 +1,75 @@ + +#ifndef __GST_TEXTOVERLAY_H__ +#define __GST_TEXTOVERLAY_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_TEXTOVERLAY (gst_textoverlay_get_type()) +#define GST_TEXTOVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ + GST_TYPE_TEXTOVERLAY, GstTextOverlay)) +#define GST_TEXTOVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\ + GST_TYPE_ULAW, GstTextOverlay)) +#define GST_TEXTOVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GST_TYPE_TEXTOVERLAY, GstTextOverlayClass)) +#define GST_IS_TEXTOVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\ + GST_TYPE_TEXTOVERLAY)) +#define GST_IS_TEXTOVERLAY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),\ + GST_TYPE_TEXTOVERLAY)) + +typedef struct _GstTextOverlay GstTextOverlay; +typedef struct _GstTextOverlayClass GstTextOverlayClass; + +typedef enum _GstTextOverlayVAlign GstTextOverlayVAlign; +typedef enum _GstTextOverlayHAlign GstTextOverlayHAlign; + +enum _GstTextOverlayVAlign { + GST_TEXT_OVERLAY_VALIGN_BASELINE, + GST_TEXT_OVERLAY_VALIGN_BOTTOM, + GST_TEXT_OVERLAY_VALIGN_TOP +}; + +enum _GstTextOverlayHAlign { + GST_TEXT_OVERLAY_HALIGN_LEFT, + GST_TEXT_OVERLAY_HALIGN_CENTER, + GST_TEXT_OVERLAY_HALIGN_RIGHT +}; + + +struct _GstTextOverlay { + GstElement element; + + GstPad *video_sinkpad; + GstPad *text_sinkpad; + GstPad *srcpad; + gint width; + gint height; + + GstTextOverlayVAlign valign; + GstTextOverlayHAlign halign; + gint x0; + gint y0; + gchar *default_text; + + cairo_t *cr; + guchar *pixbuf; + int text_width; + int text_height; + + GstBuffer *current_buffer; + GstBuffer *next_buffer; + gboolean need_render; + +}; + +struct _GstTextOverlayClass { + GstElementClass parent_class; +}; + +GType gst_textoverlay_get_type(void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GST_TEXTOVERLAY_H */ diff --git a/ext/cairo/gsttimeoverlay.c b/ext/cairo/gsttimeoverlay.c new file mode 100644 index 00000000..f3cb3dc9 --- /dev/null +++ b/ext/cairo/gsttimeoverlay.c @@ -0,0 +1,306 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2003> David Schleef + * + * 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. + */ + +/* + * This file was (probably) generated from gsttimeoverlay.c, + * gsttimeoverlay.c,v 1.7 2003/11/08 02:48:59 dschleef Exp + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/*#define DEBUG_ENABLED */ +#include +#include +#include + +#include + + +/* GstTimeoverlay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + ARG_0 + /* FILL ME */ +}; + +static void gst_timeoverlay_base_init (gpointer g_class); +static void gst_timeoverlay_class_init (gpointer g_class, gpointer class_data); +static void gst_timeoverlay_init (GTypeInstance * instance, gpointer g_class); + +static void gst_timeoverlay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_timeoverlay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_timeoverlay_planar411 (GstVideofilter * videofilter, void *dest, + void *src); +static void gst_timeoverlay_setup (GstVideofilter * videofilter); + +GType +gst_timeoverlay_get_type (void) +{ + static GType timeoverlay_type = 0; + + if (!timeoverlay_type) { + static const GTypeInfo timeoverlay_info = { + sizeof (GstTimeoverlayClass), + gst_timeoverlay_base_init, + NULL, + gst_timeoverlay_class_init, + NULL, + NULL, + sizeof (GstTimeoverlay), + 0, + gst_timeoverlay_init, + }; + + timeoverlay_type = g_type_register_static (GST_TYPE_VIDEOFILTER, + "GstTimeoverlay", &timeoverlay_info, 0); + } + return timeoverlay_type; +} + +static GstVideofilterFormat gst_timeoverlay_formats[] = { + {"I420", 12, gst_timeoverlay_planar411,}, +}; + + +static void +gst_timeoverlay_base_init (gpointer g_class) +{ + static GstElementDetails timeoverlay_details = + GST_ELEMENT_DETAILS ("Time Overlay", + "Filter/Editor/Video", + "Overlays the time on a video stream", + "David Schleef "); + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstVideofilterClass *videofilter_class = GST_VIDEOFILTER_CLASS (g_class); + int i; + + gst_element_class_set_details (element_class, &timeoverlay_details); + + for (i = 0; i < G_N_ELEMENTS (gst_timeoverlay_formats); i++) { + gst_videofilter_class_add_format (videofilter_class, + gst_timeoverlay_formats + i); + } + + gst_videofilter_class_add_pad_templates (GST_VIDEOFILTER_CLASS (g_class)); +} + +static void +gst_timeoverlay_class_init (gpointer g_class, gpointer class_data) +{ + GObjectClass *gobject_class; + GstVideofilterClass *videofilter_class; + + gobject_class = G_OBJECT_CLASS (g_class); + videofilter_class = GST_VIDEOFILTER_CLASS (g_class); + +#if 0 + g_object_class_install_property (gobject_class, ARG_METHOD, + g_param_spec_enum ("method", "method", "method", + GST_TYPE_TIMEOVERLAY_METHOD, GST_TIMEOVERLAY_METHOD_1, + G_PARAM_READWRITE)); +#endif + + gobject_class->set_property = gst_timeoverlay_set_property; + gobject_class->get_property = gst_timeoverlay_get_property; + + videofilter_class->setup = gst_timeoverlay_setup; +} + +static void +gst_timeoverlay_init (GTypeInstance * instance, gpointer g_class) +{ + GstTimeoverlay *timeoverlay = GST_TIMEOVERLAY (instance); + GstVideofilter *videofilter; + + GST_DEBUG ("gst_timeoverlay_init"); + + videofilter = GST_VIDEOFILTER (timeoverlay); + + /* do stuff */ +} + +static void +gst_timeoverlay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstTimeoverlay *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_TIMEOVERLAY (object)); + src = GST_TIMEOVERLAY (object); + + GST_DEBUG ("gst_timeoverlay_set_property"); + switch (prop_id) { +#if 0 + case ARG_METHOD: + src->method = g_value_get_enum (value); + break; +#endif + default: + break; + } +} + +static void +gst_timeoverlay_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstTimeoverlay *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_TIMEOVERLAY (object)); + src = GST_TIMEOVERLAY (object); + + switch (prop_id) { +#if 0 + case ARG_METHOD: + g_value_set_enum (value, src->method); + break; +#endif + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_timeoverlay_setup (GstVideofilter * videofilter) +{ + GstTimeoverlay *timeoverlay; + cairo_font_extents_t font_extents; + + g_return_if_fail (GST_IS_TIMEOVERLAY (videofilter)); + timeoverlay = GST_TIMEOVERLAY (videofilter); + + /* if any setup needs to be done, do it here */ + + timeoverlay->cr = cairo_create (); + + cairo_set_rgb_color (timeoverlay->cr, 0, 0, 0); + + cairo_select_font (timeoverlay->cr, "monospace", 0, 0); + cairo_scale_font (timeoverlay->cr, 20); + + cairo_current_font_extents (timeoverlay->cr, &font_extents); + timeoverlay->text_height = font_extents.height; + +} + +static char * +gst_timeoverlay_print_smpte_time (guint64 time) +{ + int hours; + int minutes; + int seconds; + int ms; + double x; + + x = rint ((time + 500000) * 1e-6); + + hours = floor (x / (60 * 60 * 1000)); + x -= hours * 60 * 60 * 1000; + minutes = floor (x / (60 * 1000)); + x -= minutes * 60 * 1000; + seconds = floor (x / (1000)); + x -= seconds * 1000; + ms = rint (x); + + return g_strdup_printf ("%02d:%02d:%02d.%03d", hours, minutes, seconds, ms); +} + +static void +gst_timeoverlay_planar411 (GstVideofilter * videofilter, void *dest, void *src) +{ + GstTimeoverlay *timeoverlay; + int width; + int height; + int b_width; + char *string; + int i, j; + uint8_t *image; + cairo_text_extents_t extents; + + g_return_if_fail (GST_IS_TIMEOVERLAY (videofilter)); + timeoverlay = GST_TIMEOVERLAY (videofilter); + + width = gst_videofilter_get_input_width (videofilter); + height = gst_videofilter_get_input_height (videofilter); + + string = + gst_timeoverlay_print_smpte_time (GST_BUFFER_TIMESTAMP (videofilter-> + in_buf)); + + image = g_malloc (4 * width * timeoverlay->text_height); + + cairo_set_target_image (timeoverlay->cr, image, CAIRO_FORMAT_ARGB32, + width, timeoverlay->text_height, width * 4); + + cairo_save (timeoverlay->cr); + cairo_rectangle (timeoverlay->cr, 0, 0, width, timeoverlay->text_height); + cairo_set_alpha (timeoverlay->cr, 0); + cairo_set_operator (timeoverlay->cr, CAIRO_OPERATOR_SRC); + cairo_fill (timeoverlay->cr); + cairo_restore (timeoverlay->cr); + + cairo_save (timeoverlay->cr); + cairo_text_extents (timeoverlay->cr, string, &extents); + + cairo_set_rgb_color (timeoverlay->cr, 1, 1, 1); + cairo_move_to (timeoverlay->cr, 0, timeoverlay->text_height - 2); + cairo_show_text (timeoverlay->cr, string); + g_free (string); +#if 0 + cairo_text_path (timeoverlay->cr, string); + cairo_set_rgb_color (timeoverlay->cr, 1, 1, 1); + cairo_set_line_width (timeoverlay->cr, 1.0); + cairo_stroke (timeoverlay->cr); +#endif + + cairo_restore (timeoverlay->cr); + + b_width = extents.width; + if (b_width > width) + b_width = width; + + memcpy (dest, src, videofilter->from_buf_size); + for (i = 0; i < timeoverlay->text_height; i++) { + for (j = 0; j < b_width; j++) { + ((uint8_t *) dest)[i * width + j] = image[(i * width + j) * 4 + 0]; + } + } + for (i = 0; i < timeoverlay->text_height / 2; i++) { + memset (dest + width * height + i * (width / 2), 128, b_width / 2); + memset (dest + width * height + (width / 2) * (height / 2) + + i * (width / 2), 128, b_width / 2); + } + + g_free (image); +} diff --git a/ext/cairo/gsttimeoverlay.h b/ext/cairo/gsttimeoverlay.h new file mode 100644 index 00000000..2f858b28 --- /dev/null +++ b/ext/cairo/gsttimeoverlay.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ + + +#ifndef __GST_TIMEOVERLAY_H__ +#define __GST_TIMEOVERLAY_H__ + + +#include +#include + +#include "gstvideofilter.h" + + +G_BEGIN_DECLS + +#define GST_TYPE_TIMEOVERLAY \ + (gst_timeoverlay_get_type()) +#define GST_TIMEOVERLAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TIMEOVERLAY,GstTimeoverlay)) +#define GST_TIMEOVERLAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TIMEOVERLAY,GstTimeoverlayClass)) +#define GST_IS_TIMEOVERLAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TIMEOVERLAY)) +#define GST_IS_TIMEOVERLAY_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TIMEOVERLAY)) + +typedef struct _GstTimeoverlay GstTimeoverlay; +typedef struct _GstTimeoverlayClass GstTimeoverlayClass; + +struct _GstTimeoverlay { + GstVideofilter videofilter; + + cairo_t *cr; + int text_height; + +}; + +struct _GstTimeoverlayClass { + GstVideofilterClass parent_class; +}; + +GType gst_timeoverlay_get_type(void); + +G_END_DECLS + +#endif /* __GST_TIMEOVERLAY_H__ */ + -- cgit