+++ /dev/null
-/* GStreamer
- *
- * Copyright (C) 2006-2009 Lutz Mueller <lutz@topfrose.de>
- *
- * 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-cairorender
- *
- * cairorender encodes a video stream into PDF, SVG, PNG or Postscript
- *
- * <refsect2>
- * <title>Example launch line</title>
- * |[
- * gst-launch-1.0 videotestsrc num-buffers=3 ! cairorender ! "application/pdf" ! filesink location=test.pdf
- * ]|
- * </refsect2>
- */
-
-#include "gstcairorender.h"
-
-#include <cairo.h>
-#include <cairo-features.h>
-#ifdef CAIRO_HAS_PS_SURFACE
-#include <cairo-ps.h>
-#endif
-#ifdef CAIRO_HAS_PDF_SURFACE
-#include <cairo-pdf.h>
-#endif
-#ifdef CAIRO_HAS_SVG_SURFACE
-#include <cairo-svg.h>
-#endif
-
-#include <gst/video/video.h>
-
-#include <string.h>
-
-GST_DEBUG_CATEGORY_STATIC (cairo_render_debug);
-#define GST_CAT_DEFAULT cairo_render_debug
-
-static gboolean
-gst_cairo_render_event (GstPad * pad, GstEvent * e)
-{
- GstCairoRender *c = GST_CAIRO_RENDER (GST_PAD_PARENT (pad));
-
- switch (GST_EVENT_TYPE (e)) {
- case GST_EVENT_EOS:
- if (c->surface)
- cairo_surface_finish (c->surface);
- break;
- default:
- break;
- }
- return gst_pad_event_default (pad, e);
-}
-
-static cairo_status_t
-write_func (void *closure, const unsigned char *data, unsigned int length)
-{
- GstCairoRender *c = GST_CAIRO_RENDER (closure);
- GstBuffer *buf;
- GstFlowReturn r;
-
- buf = gst_buffer_new ();
- gst_buffer_set_data (buf, (guint8 *) data, length);
- gst_buffer_set_caps (buf, GST_PAD_CAPS (c->src));
- if ((r = gst_pad_push (c->src, buf)) != GST_FLOW_OK) {
- GST_DEBUG_OBJECT (c, "Could not pass on buffer: %s.",
- gst_flow_get_name (r));
- return CAIRO_STATUS_WRITE_ERROR;
- }
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-read_buffer (void *closure, unsigned char *data, unsigned int length)
-{
- GstBuffer *buf = GST_BUFFER (closure);
-
- if (GST_BUFFER_OFFSET (buf) + length > GST_BUFFER_SIZE (buf))
- return CAIRO_STATUS_READ_ERROR;
- memcpy (data, GST_BUFFER_DATA (buf) + GST_BUFFER_OFFSET (buf), length);
- GST_BUFFER_OFFSET (buf) += length;
- return CAIRO_STATUS_SUCCESS;
-}
-
-static gboolean
-gst_cairo_render_push_surface (GstCairoRender * c, cairo_surface_t * surface)
-{
- cairo_status_t s = 0;
- cairo_t *cr;
-
- if (!c->surface) {
- s = cairo_surface_write_to_png_stream (surface, write_func, c);
- cairo_surface_destroy (surface);
- if (s != CAIRO_STATUS_SUCCESS) {
- GST_DEBUG_OBJECT (c, "Could not create PNG stream: %s.",
- cairo_status_to_string (s));
- return FALSE;
- }
- return TRUE;
- }
-
- cr = cairo_create (c->surface);
- cairo_set_source_surface (cr, surface, 0, 0);
- cairo_paint (cr);
- cairo_show_page (cr);
- cairo_destroy (cr);
- cairo_surface_destroy (surface);
- return (TRUE);
-}
-
-static GstFlowReturn
-gst_cairo_render_chain (GstPad * pad, GstBuffer * buf)
-{
- GstCairoRender *c = GST_CAIRO_RENDER (GST_PAD_PARENT (pad));
- cairo_surface_t *s;
- gboolean success;
-
- if (G_UNLIKELY (c->width <= 0 || c->height <= 0 || c->stride <= 0))
- return GST_FLOW_NOT_NEGOTIATED;
-
- if (c->png) {
- GST_BUFFER_OFFSET (buf) = 0;
- s = cairo_image_surface_create_from_png_stream (read_buffer, buf);
- } else {
- if (c->format == CAIRO_FORMAT_ARGB32) {
- guint i, j;
- guint8 *data = GST_BUFFER_DATA (buf);
-
- buf = gst_buffer_make_writable (buf);
-
- /* Cairo ARGB is pre-multiplied with the alpha
- * value, i.e. 0x80008000 is half transparent
- * green
- */
- for (i = 0; i < c->height; i++) {
- for (j = 0; j < c->width; j++) {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
- guint8 alpha = data[3];
-
- data[0] = (data[0] * alpha) >> 8;
- data[1] = (data[1] * alpha) >> 8;
- data[2] = (data[2] * alpha) >> 8;
-#else
- guint8 alpha = data[0];
-
- data[1] = (data[1] * alpha) >> 8;
- data[2] = (data[2] * alpha) >> 8;
- data[3] = (data[3] * alpha) >> 8;
-#endif
- data += 4;
- }
- }
- }
-
- s = cairo_image_surface_create_for_data (GST_BUFFER_DATA (buf),
- c->format, c->width, c->height, c->stride);
- }
-
- success = gst_cairo_render_push_surface (c, s);
- gst_buffer_unref (buf);
- return success ? GST_FLOW_OK : GST_FLOW_ERROR;
-}
-
-static gboolean
-gst_cairo_render_setcaps_sink (GstPad * pad, GstCaps * caps)
-{
- GstCairoRender *c = GST_CAIRO_RENDER (GST_PAD_PARENT (pad));
- GstStructure *s = gst_caps_get_structure (caps, 0);
- const gchar *mime = gst_structure_get_name (s);
- gint fps_n = 0, fps_d = 1;
- gint w, h;
-
- GST_DEBUG_OBJECT (c, "Got caps (%s).", mime);
- if ((c->png = !strcmp (mime, "image/png")))
- return TRUE;
-
- /* Width and height */
- if (!gst_structure_get_int (s, "width", &c->width) ||
- !gst_structure_get_int (s, "height", &c->height)) {
- GST_ERROR_OBJECT (c, "Invalid caps");
- return FALSE;
- }
-
- /* Colorspace */
- if (!strcmp (mime, "video/x-raw-yuv") || !strcmp (mime, "video/x-raw-grey")) {
- c->format = CAIRO_FORMAT_A8;
- c->stride = GST_ROUND_UP_4 (c->width);
- } else if (!strcmp (mime, "video/x-raw-rgb")) {
- gint bpp;
-
- if (!gst_structure_get_int (s, "bpp", &bpp)) {
- GST_ERROR_OBJECT (c, "Invalid caps");
- return FALSE;
- }
-
- c->format = (bpp == 32) ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24;
- c->stride = 4 * c->width;
- } else {
- GST_DEBUG_OBJECT (c, "Unknown mime type '%s'.", mime);
- return FALSE;
- }
-
- /* Framerate */
- gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d);
-
- /* Create output caps */
- caps = gst_pad_get_allowed_caps (c->src);
- caps = gst_caps_make_writable (caps);
- gst_caps_truncate (caps);
- s = gst_caps_get_structure (caps, 0);
- mime = gst_structure_get_name (s);
- gst_structure_set (s, "height", G_TYPE_INT, c->height, "width", G_TYPE_INT,
- c->width, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
-
- if (c->surface) {
- cairo_surface_destroy (c->surface);
- c->surface = NULL;
- }
-
- w = c->width;
- h = c->height;
-
- GST_DEBUG_OBJECT (c, "Setting src caps %" GST_PTR_FORMAT, caps);
- gst_pad_set_caps (c->src, caps);
-
-#if CAIRO_HAS_PS_SURFACE
- if (!strcmp (mime, "application/postscript")) {
- c->surface = cairo_ps_surface_create_for_stream (write_func, c, w, h);
- } else
-#endif
-#if CAIRO_HAS_PDF_SURFACE
- if (!strcmp (mime, "application/pdf")) {
- c->surface = cairo_pdf_surface_create_for_stream (write_func, c, w, h);
- } else
-#endif
-#if CAIRO_HAS_SVG_SURFACE
- if (!strcmp (mime, "image/svg+xml")) {
- c->surface = cairo_svg_surface_create_for_stream (write_func, c, w, h);
- } else
-#endif
- {
- gst_caps_unref (caps);
- return FALSE;
- }
-
- gst_caps_unref (caps);
-
- return TRUE;
-}
-
-
-#define SIZE_CAPS "width = (int) [ 1, MAX], height = (int) [ 1, MAX] "
-#if CAIRO_HAS_PDF_SURFACE
-#define PDF_CAPS "application/pdf, " SIZE_CAPS
-#else
-#define PDF_CAPS
-#endif
-#if CAIRO_HAS_PDF_SURFACE && (CAIRO_HAS_PS_SURFACE || CAIRO_HAS_SVG_SURFACE || CAIRO_HAS_PNG_FUNCTIONS)
-#define JOIN1 ";"
-#else
-#define JOIN1
-#endif
-#if CAIRO_HAS_PS_SURFACE
-#define PS_CAPS "application/postscript, " SIZE_CAPS
-#else
-#define PS_CAPS
-#endif
-#if (CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_PS_SURFACE) && (CAIRO_HAS_SVG_SURFACE || CAIRO_HAS_PNG_FUNCTIONS)
-#define JOIN2 ";"
-#else
-#define JOIN2
-#endif
-#if CAIRO_HAS_SVG_SURFACE
-#define SVG_CAPS "image/svg+xml, " SIZE_CAPS
-#else
-#define SVG_CAPS
-#endif
-#if (CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_PS_SURFACE || CAIRO_HAS_SVG_SURFACE) && CAIRO_HAS_PNG_FUNCTIONS
-#define JOIN3 ";"
-#else
-#define JOIN3
-#endif
-#if CAIRO_HAS_PNG_FUNCTIONS
-#define PNG_CAPS "image/png, " SIZE_CAPS
-#define PNG_CAPS2 "; image/png, " SIZE_CAPS
-#else
-#define PNG_CAPS
-#define PNG_CAPS2
-#endif
-
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-#define ARGB_CAPS GST_VIDEO_CAPS_BGRx " ; " GST_VIDEO_CAPS_BGRA " ; "
-#else
-#define ARGB_CAPS GST_VIDEO_CAPS_xRGB " ; " GST_VIDEO_CAPS_ARGB " ; "
-#endif
-static GstStaticPadTemplate t_src = GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC, GST_PAD_ALWAYS,
- GST_STATIC_CAPS (PDF_CAPS JOIN1 PS_CAPS JOIN2 SVG_CAPS JOIN3 PNG_CAPS));
-static GstStaticPadTemplate t_snk = GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS (ARGB_CAPS
- GST_VIDEO_CAPS_YUV ("Y800") " ; "
- "video/x-raw-gray, "
- "bpp = 8, "
- "depth = 8, "
- "width = " GST_VIDEO_SIZE_RANGE ", "
- "height = " GST_VIDEO_SIZE_RANGE ", " "framerate = " GST_VIDEO_FPS_RANGE
- PNG_CAPS2));
-
-GST_BOILERPLATE (GstCairoRender, gst_cairo_render, GstElement,
- GST_TYPE_ELEMENT);
-
-static void
-gst_cairo_render_init (GstCairoRender * c, GstCairoRenderClass * klass)
-{
- /* The sink */
- c->snk = gst_pad_new_from_static_template (&t_snk, "sink");
- gst_pad_set_event_function (c->snk, gst_cairo_render_event);
- gst_pad_set_chain_function (c->snk, gst_cairo_render_chain);
- gst_pad_set_setcaps_function (c->snk, gst_cairo_render_setcaps_sink);
- gst_pad_use_fixed_caps (c->snk);
- gst_element_add_pad (GST_ELEMENT (c), c->snk);
-
- /* The source */
- c->src = gst_pad_new_from_static_template (&t_src, "src");
- gst_pad_use_fixed_caps (c->src);
- gst_element_add_pad (GST_ELEMENT (c), c->src);
-
- c->width = 0;
- c->height = 0;
- c->stride = 0;
-}
-
-static void
-gst_cairo_render_base_init (gpointer g_class)
-{
- GstElementClass *ec = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_set_static_metadata (ec, "Cairo encoder",
- "Codec/Encoder", "Encodes streams using Cairo",
- "Lutz Mueller <lutz@topfrose.de>");
- gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&t_snk));
- gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&t_src));
-}
-
-static void
-gst_cairo_render_finalize (GObject * object)
-{
- GstCairoRender *c = GST_CAIRO_RENDER (object);
-
- if (c->surface) {
- cairo_surface_destroy (c->surface);
- c->surface = NULL;
- }
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_cairo_render_class_init (GstCairoRenderClass * klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
- gobject_class->finalize = gst_cairo_render_finalize;
-
- GST_DEBUG_CATEGORY_INIT (cairo_render_debug, "cairo_render", 0,
- "Cairo encoder");
-}
+++ /dev/null
-/* 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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-/**
- * SECTION:element-cairotextoverlay
- *
- * cairotextoverlay renders the text on top of the video frames.
- *
- * <refsect2>
- * <title>Example launch line</title>
- * |[
- * gst-launch-1.0 videotestsrc ! cairotextoverlay text="hello" ! autovideosink
- * ]|
- * </refsect2>
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <string.h>
-#include <gst/video/video.h>
-#include "gsttextoverlay.h"
-
-#include <cairo.h>
-
-/* FIXME:
- * - calculating the position of the shading rectangle is
- * not really right (try with text "L"), to say the least.
- * Seems to work at least with latin script though.
- * - check final x/y position and text width/height so that
- * we don't do out-of-memory access when blitting the text.
- * Also, we do not want to blit over the right or left margin.
- * - what about text with newline characters? Cairo doesn't deal
- * with that (we'd need to fix text_height usage for that as well)
- * - upstream caps renegotiation, ie. when video window gets resized
- */
-
-GST_DEBUG_CATEGORY_EXTERN (cairo_debug);
-#define GST_CAT_DEFAULT cairo_debug
-
-enum
-{
- ARG_0,
- ARG_TEXT,
- ARG_SHADING,
- ARG_VALIGN,
- ARG_HALIGN,
- ARG_XPAD,
- ARG_YPAD,
- ARG_DELTAX,
- ARG_DELTAY,
- ARG_SILENT,
- ARG_FONT_DESC
-};
-
-#define DEFAULT_YPAD 25
-#define DEFAULT_XPAD 25
-#define DEFAULT_FONT "sans"
-#define DEFAULT_SILENT FALSE
-
-#define GST_CAIRO_TEXT_OVERLAY_DEFAULT_SCALE 20.0
-
-static GstStaticPadTemplate cairo_text_overlay_src_template_factory =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
- );
-
-static GstStaticPadTemplate video_sink_template_factory =
-GST_STATIC_PAD_TEMPLATE ("video_sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
- );
-
-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_text_overlay_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec);
-static GstStateChangeReturn gst_text_overlay_change_state (GstElement * element,
- GstStateChange transition);
-static GstCaps *gst_text_overlay_getcaps (GstPad * pad);
-static gboolean gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps);
-static GstPadLinkReturn gst_text_overlay_text_pad_linked (GstPad * pad,
- GstPad * peer);
-static void gst_text_overlay_text_pad_unlinked (GstPad * pad);
-static GstFlowReturn gst_text_overlay_collected (GstCollectPads * pads,
- gpointer data);
-static void gst_text_overlay_finalize (GObject * object);
-static void gst_text_overlay_font_init (GstCairoTextOverlay * overlay);
-static gboolean gst_text_overlay_src_event (GstPad * pad, GstEvent * event);
-static gboolean gst_text_overlay_video_event (GstPad * pad, GstEvent * event);
-
-/* These macros are adapted from videotestsrc.c */
-#define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
-#define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
-#define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)
-
-#define I420_Y_OFFSET(w,h) (0)
-#define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
-#define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
-
-#define I420_SIZE(w,h) (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
-
-GST_BOILERPLATE (GstCairoTextOverlay, gst_text_overlay, GstElement,
- GST_TYPE_ELEMENT);
-
-static void
-gst_text_overlay_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 (&cairo_text_overlay_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_static_metadata (element_class, "Text overlay",
- "Filter/Editor/Video",
- "Adds text strings on top of a video buffer",
- "David Schleef <ds@schleef.org>");
-}
-
-static void
-gst_text_overlay_class_init (GstCairoTextOverlayClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
-
- gobject_class->finalize = gst_text_overlay_finalize;
- gobject_class->set_property = gst_text_overlay_set_property;
-
- gstelement_class->change_state =
- GST_DEBUG_FUNCPTR (gst_text_overlay_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_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SHADING,
- g_param_spec_boolean ("shaded-background", "shaded background",
- "Whether to shade the background under the text area", FALSE,
- G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
- 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_PARAM_STATIC_STRINGS));
- 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_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_XPAD,
- g_param_spec_int ("xpad", "horizontal paddding",
- "Horizontal paddding when using left/right alignment",
- G_MININT, G_MAXINT, DEFAULT_XPAD,
- G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_YPAD,
- g_param_spec_int ("ypad", "vertical padding",
- "Vertical padding when using top/bottom alignment",
- G_MININT, G_MAXINT, DEFAULT_YPAD,
- G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DELTAX,
- g_param_spec_int ("deltax", "X position modifier",
- "Shift X position to the left or to the right. Unit is pixels.",
- G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DELTAY,
- g_param_spec_int ("deltay", "Y position modifier",
- "Shift Y position up or down. Unit is pixels.",
- G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
- 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));
- /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
- g_param_spec_boolean ("silent", "silent",
- "Whether to render the text string",
- DEFAULT_SILENT, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
-}
-
-static void
-gst_text_overlay_finalize (GObject * object)
-{
- GstCairoTextOverlay *overlay = GST_CAIRO_TEXT_OVERLAY (object);
-
- gst_collect_pads_stop (overlay->collect);
- gst_object_unref (overlay->collect);
-
- g_free (overlay->text_fill_image);
- g_free (overlay->text_outline_image);
-
- g_free (overlay->default_text);
- g_free (overlay->font);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_text_overlay_init (GstCairoTextOverlay * overlay,
- GstCairoTextOverlayClass * klass)
-{
- /* video sink */
- overlay->video_sinkpad =
- gst_pad_new_from_static_template (&video_sink_template_factory,
- "video_sink");
- gst_pad_set_getcaps_function (overlay->video_sinkpad,
- GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
- gst_pad_set_setcaps_function (overlay->video_sinkpad,
- GST_DEBUG_FUNCPTR (gst_text_overlay_setcaps));
- gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
-
- /* text sink */
- overlay->text_sinkpad =
- gst_pad_new_from_static_template (&text_sink_template_factory,
- "text_sink");
- gst_pad_set_link_function (overlay->text_sinkpad,
- GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_linked));
- gst_pad_set_unlink_function (overlay->text_sinkpad,
- GST_DEBUG_FUNCPTR (gst_text_overlay_text_pad_unlinked));
- gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
-
- /* (video) source */
- overlay->srcpad =
- gst_pad_new_from_static_template
- (&cairo_text_overlay_src_template_factory, "src");
- gst_pad_set_getcaps_function (overlay->srcpad,
- GST_DEBUG_FUNCPTR (gst_text_overlay_getcaps));
- gst_pad_set_event_function (overlay->srcpad,
- GST_DEBUG_FUNCPTR (gst_text_overlay_src_event));
- gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
-
- overlay->halign = GST_CAIRO_TEXT_OVERLAY_HALIGN_CENTER;
- overlay->valign = GST_CAIRO_TEXT_OVERLAY_VALIGN_BASELINE;
- overlay->xpad = DEFAULT_XPAD;
- overlay->ypad = DEFAULT_YPAD;
- overlay->deltax = 0;
- overlay->deltay = 0;
-
- overlay->default_text = g_strdup ("");
- overlay->need_render = TRUE;
-
- overlay->font = g_strdup (DEFAULT_FONT);
- gst_text_overlay_font_init (overlay);
-
- overlay->silent = DEFAULT_SILENT;
-
- overlay->fps_n = 0;
- overlay->fps_d = 1;
-
- overlay->collect = gst_collect_pads_new ();
-
- gst_collect_pads_set_function (overlay->collect,
- GST_DEBUG_FUNCPTR (gst_text_overlay_collected), overlay);
-
- overlay->video_collect_data = gst_collect_pads_add_pad (overlay->collect,
- overlay->video_sinkpad, sizeof (GstCollectData), NULL, TRUE);
-
- /* FIXME: hacked way to override/extend the event function of
- * GstCollectPads; because it sets its own event function giving the
- * element no access to events. Nicked from avimux. */
- overlay->collect_event =
- (GstPadEventFunction) GST_PAD_EVENTFUNC (overlay->video_sinkpad);
- gst_pad_set_event_function (overlay->video_sinkpad,
- GST_DEBUG_FUNCPTR (gst_text_overlay_video_event));
-
- /* text pad will be added when it is linked */
- overlay->text_collect_data = NULL;
-}
-
-static void
-gst_text_overlay_font_init (GstCairoTextOverlay * overlay)
-{
- cairo_font_extents_t font_extents;
- cairo_surface_t *surface;
- cairo_t *cr;
- gchar *font_desc, *sep;
-
- font_desc = g_ascii_strdown (overlay->font, -1);
-
- /* cairo_select_font_face() does not parse the size at the end, so we have
- * to do that ourselves; same for slate and weight */
- sep = MAX (strrchr (font_desc, ' '), strrchr (font_desc, ','));
- if (sep != NULL && g_strtod (sep, NULL) > 0.0) {
- /* there may be a suffix such as 'px', but we just ignore that for now */
- overlay->scale = g_strtod (sep, NULL);
- } else {
- overlay->scale = GST_CAIRO_TEXT_OVERLAY_DEFAULT_SCALE;
- }
- if (strstr (font_desc, "bold"))
- overlay->weight = CAIRO_FONT_WEIGHT_BOLD;
- else
- overlay->weight = CAIRO_FONT_WEIGHT_NORMAL;
-
- if (strstr (font_desc, "italic"))
- overlay->slant = CAIRO_FONT_SLANT_ITALIC;
- else if (strstr (font_desc, "oblique"))
- overlay->slant = CAIRO_FONT_SLANT_OBLIQUE;
- else
- overlay->slant = CAIRO_FONT_SLANT_NORMAL;
-
- GST_LOG_OBJECT (overlay, "Font desc: '%s', scale=%f, weight=%d, slant=%d",
- overlay->font, overlay->scale, overlay->weight, overlay->slant);
-
- surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 256, 256);
- cr = cairo_create (surface);
-
- cairo_select_font_face (cr, overlay->font, overlay->slant, overlay->weight);
- cairo_set_font_size (cr, overlay->scale);
-
- /* this has a static leak:
- * http://lists.freedesktop.org/archives/cairo/2007-May/010623.html
- */
- cairo_font_extents (cr, &font_extents);
- overlay->font_height = GST_ROUND_UP_2 ((guint) font_extents.height);
- overlay->need_render = TRUE;
-
- cairo_destroy (cr);
- cairo_surface_destroy (surface);
- g_free (font_desc);
-}
-
-static void
-gst_text_overlay_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstCairoTextOverlay *overlay = GST_CAIRO_TEXT_OVERLAY (object);
-
- GST_OBJECT_LOCK (overlay);
-
- switch (prop_id) {
- case ARG_TEXT:{
- g_free (overlay->default_text);
- overlay->default_text = g_value_dup_string (value);
- break;
- }
- case ARG_SHADING:{
- overlay->want_shading = g_value_get_boolean (value);
- break;
- }
- case ARG_VALIGN:{
- const gchar *s = g_value_get_string (value);
-
- if (g_ascii_strcasecmp (s, "baseline") == 0)
- overlay->valign = GST_CAIRO_TEXT_OVERLAY_VALIGN_BASELINE;
- else if (g_ascii_strcasecmp (s, "bottom") == 0)
- overlay->valign = GST_CAIRO_TEXT_OVERLAY_VALIGN_BOTTOM;
- else if (g_ascii_strcasecmp (s, "top") == 0)
- overlay->valign = GST_CAIRO_TEXT_OVERLAY_VALIGN_TOP;
- else
- g_warning ("Invalid 'valign' property value: %s", s);
- break;
- }
- case ARG_HALIGN:{
- const gchar *s = g_value_get_string (value);
-
- if (g_ascii_strcasecmp (s, "left") == 0)
- overlay->halign = GST_CAIRO_TEXT_OVERLAY_HALIGN_LEFT;
- else if (g_ascii_strcasecmp (s, "right") == 0)
- overlay->halign = GST_CAIRO_TEXT_OVERLAY_HALIGN_RIGHT;
- else if (g_ascii_strcasecmp (s, "center") == 0)
- overlay->halign = GST_CAIRO_TEXT_OVERLAY_HALIGN_CENTER;
- else
- g_warning ("Invalid 'halign' property value: %s", s);
- break;
- }
- case ARG_XPAD:{
- overlay->xpad = g_value_get_int (value);
- break;
- }
- case ARG_YPAD:{
- overlay->ypad = g_value_get_int (value);
- break;
- }
- case ARG_DELTAX:{
- overlay->deltax = g_value_get_int (value);
- break;
- }
- case ARG_DELTAY:{
- overlay->deltay = g_value_get_int (value);
- break;
- }
- case ARG_FONT_DESC:{
- g_free (overlay->font);
- overlay->font = g_value_dup_string (value);
- if (overlay->font == NULL)
- overlay->font = g_strdup (DEFAULT_FONT);
- gst_text_overlay_font_init (overlay);
- break;
- }
- case ARG_SILENT:
- overlay->silent = g_value_get_boolean (value);
- break;
- default:{
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
- }
-
- overlay->need_render = TRUE;
-
- GST_OBJECT_UNLOCK (overlay);
-}
-
-static void
-gst_text_overlay_render_text (GstCairoTextOverlay * overlay,
- const gchar * text, gint textlen)
-{
- cairo_text_extents_t extents;
- cairo_surface_t *surface;
- cairo_t *cr;
- gchar *string;
- double x, y;
-
- if (overlay->silent) {
- GST_DEBUG_OBJECT (overlay, "Silent mode, not rendering");
- return;
- }
-
- if (textlen < 0)
- textlen = strlen (text);
-
- if (!overlay->need_render) {
- GST_DEBUG ("Using previously rendered text.");
- g_return_if_fail (overlay->text_fill_image != NULL);
- g_return_if_fail (overlay->text_outline_image != NULL);
- return;
- }
-
- string = g_strndup (text, textlen);
- GST_DEBUG ("Rendering text '%s' on cairo RGBA surface", string);
-
- overlay->text_fill_image =
- g_realloc (overlay->text_fill_image,
- 4 * overlay->width * overlay->font_height);
-
- surface = cairo_image_surface_create_for_data (overlay->text_fill_image,
- CAIRO_FORMAT_ARGB32, overlay->width, overlay->font_height,
- overlay->width * 4);
-
- cr = cairo_create (surface);
-
- cairo_select_font_face (cr, overlay->font, overlay->slant, overlay->weight);
- cairo_set_font_size (cr, overlay->scale);
-
- cairo_save (cr);
- cairo_rectangle (cr, 0, 0, overlay->width, overlay->font_height);
- cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
-
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_fill (cr);
- cairo_restore (cr);
-
- cairo_save (cr);
- cairo_text_extents (cr, string, &extents);
- cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
-
- switch (overlay->halign) {
- case GST_CAIRO_TEXT_OVERLAY_HALIGN_LEFT:
- x = overlay->xpad;
- break;
- case GST_CAIRO_TEXT_OVERLAY_HALIGN_CENTER:
- x = (overlay->width - extents.width) / 2;
- break;
- case GST_CAIRO_TEXT_OVERLAY_HALIGN_RIGHT:
- x = overlay->width - extents.width - overlay->xpad;
- break;
- default:
- x = 0;
- }
- x += overlay->deltax;
-
- overlay->text_x0 = x;
- overlay->text_x1 = x + extents.x_advance;
-
- overlay->text_dy = (extents.height + extents.y_bearing);
- y = overlay->font_height - overlay->text_dy;
-
- cairo_move_to (cr, x, y);
- cairo_show_text (cr, string);
- cairo_restore (cr);
-
- cairo_destroy (cr);
- cairo_surface_destroy (surface);
-
- /* ----------- */
-
- overlay->text_outline_image =
- g_realloc (overlay->text_outline_image,
- 4 * overlay->width * overlay->font_height);
-
- surface = cairo_image_surface_create_for_data (overlay->text_outline_image,
- CAIRO_FORMAT_ARGB32, overlay->width, overlay->font_height,
- overlay->width * 4);
-
- cr = cairo_create (surface);
-
- cairo_select_font_face (cr, overlay->font, overlay->slant, overlay->weight);
- cairo_set_font_size (cr, overlay->scale);
-
- cairo_save (cr);
- cairo_rectangle (cr, 0, 0, overlay->width, overlay->font_height);
- cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_fill (cr);
- cairo_restore (cr);
-
- cairo_save (cr);
- cairo_move_to (cr, x, y);
- cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
- cairo_set_line_width (cr, 1.0);
- cairo_text_path (cr, string);
- cairo_stroke (cr);
- cairo_restore (cr);
-
- g_free (string);
-
- cairo_destroy (cr);
- cairo_surface_destroy (surface);
-
- overlay->need_render = FALSE;
-}
-
-static GstCaps *
-gst_text_overlay_getcaps (GstPad * pad)
-{
- GstCairoTextOverlay *overlay;
- GstPad *otherpad;
- GstCaps *caps;
-
- overlay = GST_CAIRO_TEXT_OVERLAY (gst_pad_get_parent (pad));
-
- if (pad == overlay->srcpad)
- otherpad = overlay->video_sinkpad;
- else
- otherpad = overlay->srcpad;
-
- /* we can do what the peer can */
- caps = gst_pad_peer_get_caps (otherpad);
- if (caps) {
- GstCaps *temp;
- const GstCaps *templ;
-
- GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, caps);
-
- /* filtered against our padtemplate */
- templ = gst_pad_get_pad_template_caps (otherpad);
- GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
- temp = gst_caps_intersect (caps, templ);
- GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
- gst_caps_unref (caps);
- /* this is what we can do */
- caps = temp;
- } else {
- /* no peer, our padtemplate is enough then */
- caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
- }
-
- GST_DEBUG_OBJECT (overlay, "returning %" GST_PTR_FORMAT, caps);
-
- gst_object_unref (overlay);
-
- return caps;
-}
-
-/* FIXME: upstream nego (e.g. when the video window is resized) */
-
-static gboolean
-gst_text_overlay_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstCairoTextOverlay *overlay;
- GstStructure *structure;
- gboolean ret = FALSE;
- const GValue *fps;
-
- if (!GST_PAD_IS_SINK (pad))
- return TRUE;
-
- g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
-
- overlay = GST_CAIRO_TEXT_OVERLAY (gst_pad_get_parent (pad));
-
- overlay->width = 0;
- overlay->height = 0;
- structure = gst_caps_get_structure (caps, 0);
- fps = gst_structure_get_value (structure, "framerate");
-
- if (gst_structure_get_int (structure, "width", &overlay->width) &&
- gst_structure_get_int (structure, "height", &overlay->height) &&
- fps != NULL) {
- ret = gst_pad_set_caps (overlay->srcpad, caps);
- }
-
- overlay->fps_n = gst_value_get_fraction_numerator (fps);
- overlay->fps_d = gst_value_get_fraction_denominator (fps);
-
- gst_object_unref (overlay);
-
- return ret;
-}
-
-static GstPadLinkReturn
-gst_text_overlay_text_pad_linked (GstPad * pad, GstPad * peer)
-{
- GstCairoTextOverlay *overlay;
-
- overlay = GST_CAIRO_TEXT_OVERLAY (GST_PAD_PARENT (pad));
-
- GST_DEBUG_OBJECT (overlay, "Text pad linked");
-
- if (overlay->text_collect_data == NULL) {
- overlay->text_collect_data = gst_collect_pads_add_pad (overlay->collect,
- overlay->text_sinkpad, sizeof (GstCollectData), NULL, TRUE);
- }
-
- overlay->need_render = TRUE;
-
- return GST_PAD_LINK_OK;
-}
-
-static void
-gst_text_overlay_text_pad_unlinked (GstPad * pad)
-{
- GstCairoTextOverlay *overlay;
-
- /* don't use gst_pad_get_parent() here, will deadlock */
- overlay = GST_CAIRO_TEXT_OVERLAY (GST_PAD_PARENT (pad));
-
- GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
-
- if (overlay->text_collect_data) {
- gst_collect_pads_remove_pad (overlay->collect, overlay->text_sinkpad);
- overlay->text_collect_data = NULL;
- }
-
- overlay->need_render = TRUE;
-}
-
-#define BOX_SHADING_VAL -80
-#define BOX_XPAD 6
-#define BOX_YPAD 6
-
-static inline void
-gst_text_overlay_shade_y (GstCairoTextOverlay * overlay, guchar * dest,
- guint dest_stride, gint y0, gint y1)
-{
- gint i, j, x0, x1;
-
- x0 = CLAMP (overlay->text_x0 - BOX_XPAD, 0, overlay->width);
- x1 = CLAMP (overlay->text_x1 + BOX_XPAD, 0, overlay->width);
-
- y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
- y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
-
- for (i = y0; i < y1; ++i) {
- for (j = x0; j < x1; ++j) {
- gint y = dest[(i * dest_stride) + j] + BOX_SHADING_VAL;
-
- dest[(i * dest_stride) + j] = CLAMP (y, 0, 255);
- }
- }
-}
-
-static inline void
-gst_text_overlay_blit_1 (GstCairoTextOverlay * overlay, guchar * dest,
- guchar * text_image, gint val, guint dest_stride, gint y0)
-{
- gint i, j;
- gint x, a, y;
- gint y1;
-
- y = val;
- y0 = MIN (y0, overlay->height);
- y1 = MIN (y0 + overlay->font_height, overlay->height);
-
- for (i = y0; i < y1; i++) {
- for (j = 0; j < overlay->width; j++) {
- x = dest[i * dest_stride + j];
- a = text_image[4 * ((i - y0) * overlay->width + j) + 1];
- dest[i * dest_stride + j] = (y * a + x * (255 - a)) / 255;
- }
- }
-}
-
-static inline void
-gst_text_overlay_blit_sub2x2 (GstCairoTextOverlay * overlay, guchar * dest,
- guchar * text_image, gint val, guint dest_stride, gint y0)
-{
- gint i, j;
- gint x, a, y;
- gint y1;
-
- y0 = MIN (y0, overlay->height);
- y1 = MIN (y0 + overlay->font_height, overlay->height);
-
- y = val;
-
- for (i = y0; i < y1; i += 2) {
- for (j = 0; j < overlay->width; j += 2) {
- x = dest[(i / 2) * dest_stride + j / 2];
- a = (text_image[4 * ((i - y0) * overlay->width + j) + 1] +
- text_image[4 * ((i - y0) * overlay->width + j + 1) + 1] +
- text_image[4 * ((i - y0 + 1) * overlay->width + j) + 1] +
- text_image[4 * ((i - y0 + 1) * overlay->width + j + 1) + 1] + 2) / 4;
- dest[(i / 2) * dest_stride + j / 2] = (y * a + x * (255 - a)) / 255;
- }
- }
-}
-
-
-static GstFlowReturn
-gst_text_overlay_push_frame (GstCairoTextOverlay * overlay,
- GstBuffer * video_frame)
-{
- guchar *y, *u, *v;
- gint ypos;
-
- video_frame = gst_buffer_make_writable (video_frame);
-
- switch (overlay->valign) {
- case GST_CAIRO_TEXT_OVERLAY_VALIGN_BOTTOM:
- ypos = overlay->height - overlay->font_height - overlay->ypad;
- break;
- case GST_CAIRO_TEXT_OVERLAY_VALIGN_BASELINE:
- ypos = overlay->height - (overlay->font_height - overlay->text_dy)
- - overlay->ypad;
- break;
- case GST_CAIRO_TEXT_OVERLAY_VALIGN_TOP:
- ypos = overlay->ypad;
- break;
- default:
- ypos = overlay->ypad;
- break;
- }
-
- ypos += overlay->deltay;
-
- y = GST_BUFFER_DATA (video_frame);
- u = y + I420_U_OFFSET (overlay->width, overlay->height);
- v = y + I420_V_OFFSET (overlay->width, overlay->height);
-
- /* shaded background box */
- if (overlay->want_shading) {
- gst_text_overlay_shade_y (overlay,
- y, I420_Y_ROWSTRIDE (overlay->width),
- ypos + overlay->text_dy, ypos + overlay->font_height);
- }
-
- /* blit outline text on video image */
- gst_text_overlay_blit_1 (overlay,
- y,
- overlay->text_outline_image, 0, I420_Y_ROWSTRIDE (overlay->width), ypos);
- gst_text_overlay_blit_sub2x2 (overlay,
- u,
- overlay->text_outline_image, 128, I420_U_ROWSTRIDE (overlay->width),
- ypos);
- gst_text_overlay_blit_sub2x2 (overlay, v, overlay->text_outline_image, 128,
- I420_V_ROWSTRIDE (overlay->width), ypos);
-
- /* blit text on video image */
- gst_text_overlay_blit_1 (overlay,
- y,
- overlay->text_fill_image, 255, I420_Y_ROWSTRIDE (overlay->width), ypos);
- gst_text_overlay_blit_sub2x2 (overlay,
- u,
- overlay->text_fill_image, 128, I420_U_ROWSTRIDE (overlay->width), ypos);
- gst_text_overlay_blit_sub2x2 (overlay,
- v,
- overlay->text_fill_image, 128, I420_V_ROWSTRIDE (overlay->width), ypos);
-
- return gst_pad_push (overlay->srcpad, video_frame);
-}
-
-static void
-gst_text_overlay_pop_video (GstCairoTextOverlay * overlay)
-{
- GstBuffer *buf;
-
- buf = gst_collect_pads_pop (overlay->collect, overlay->video_collect_data);
- g_return_if_fail (buf != NULL);
- gst_buffer_unref (buf);
-}
-
-static void
-gst_text_overlay_pop_text (GstCairoTextOverlay * overlay)
-{
- GstBuffer *buf;
-
- if (overlay->text_collect_data) {
- buf = gst_collect_pads_pop (overlay->collect, overlay->text_collect_data);
- g_return_if_fail (buf != NULL);
- gst_buffer_unref (buf);
- }
-
- overlay->need_render = TRUE;
-}
-
-/* This function is called when there is data on all pads */
-static GstFlowReturn
-gst_text_overlay_collected (GstCollectPads * pads, gpointer data)
-{
- GstCairoTextOverlay *overlay;
- GstFlowReturn ret = GST_FLOW_OK;
- GstClockTime now, txt_end, frame_end;
- GstBuffer *video_frame = NULL;
- GstBuffer *text_buf = NULL;
- gchar *text;
- gint text_len;
-
- overlay = GST_CAIRO_TEXT_OVERLAY (data);
-
- GST_DEBUG ("Collecting");
-
- video_frame = gst_collect_pads_peek (overlay->collect,
- overlay->video_collect_data);
-
- /* send EOS if video stream EOSed regardless of text stream */
- if (video_frame == NULL) {
- GST_DEBUG ("Video stream at EOS");
- if (overlay->text_collect_data) {
- text_buf = gst_collect_pads_pop (overlay->collect,
- overlay->text_collect_data);
- }
- gst_pad_push_event (overlay->srcpad, gst_event_new_eos ());
- ret = GST_FLOW_EOS;
- goto done;
- }
-
- if (GST_BUFFER_TIMESTAMP (video_frame) == GST_CLOCK_TIME_NONE) {
- g_warning ("%s: video frame has invalid timestamp", G_STRLOC);
- }
-
- now = GST_BUFFER_TIMESTAMP (video_frame);
-
- if (GST_BUFFER_DURATION (video_frame) != GST_CLOCK_TIME_NONE) {
- frame_end = now + GST_BUFFER_DURATION (video_frame);
- } else if (overlay->fps_n > 0) {
- frame_end = now + gst_util_uint64_scale_int (GST_SECOND,
- overlay->fps_d, overlay->fps_n);
- } else {
- /* magic value, does not really matter since texts
- * tend to span quite a few frames in practice anyway */
- frame_end = now + GST_SECOND / 25;
- }
-
- GST_DEBUG ("Got video frame: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
- GST_TIME_ARGS (now), GST_TIME_ARGS (frame_end));
-
- /* text pad not linked? */
- if (overlay->text_collect_data == NULL) {
- GST_DEBUG ("Text pad not linked, rendering default text: '%s'",
- GST_STR_NULL (overlay->default_text));
- if (overlay->default_text && *overlay->default_text != '\0') {
- gst_text_overlay_render_text (overlay, overlay->default_text, -1);
- ret = gst_text_overlay_push_frame (overlay, video_frame);
- } else {
- ret = gst_pad_push (overlay->srcpad, video_frame);
- }
- gst_text_overlay_pop_video (overlay);
- video_frame = NULL;
- goto done;
- }
-
- text_buf = gst_collect_pads_peek (overlay->collect,
- overlay->text_collect_data);
-
- /* just push the video frame if the text stream has EOSed */
- if (text_buf == NULL) {
- GST_DEBUG ("Text pad EOSed, just pushing video frame as is");
- ret = gst_pad_push (overlay->srcpad, video_frame);
- gst_text_overlay_pop_video (overlay);
- video_frame = NULL;
- goto done;
- }
-
- /* if the text buffer isn't stamped right, pop it off the
- * queue and display it for the current video frame only */
- if (GST_BUFFER_TIMESTAMP (text_buf) == GST_CLOCK_TIME_NONE ||
- GST_BUFFER_DURATION (text_buf) == GST_CLOCK_TIME_NONE) {
- GST_WARNING ("Got text buffer with invalid time stamp or duration");
- gst_text_overlay_pop_text (overlay);
- GST_BUFFER_TIMESTAMP (text_buf) = now;
- GST_BUFFER_DURATION (text_buf) = frame_end - now;
- }
-
- txt_end = GST_BUFFER_TIMESTAMP (text_buf) + GST_BUFFER_DURATION (text_buf);
-
- GST_DEBUG ("Got text buffer: %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (text_buf)), GST_TIME_ARGS (txt_end));
-
- /* if the text buffer is too old, pop it off the
- * queue and return so we get a new one next time */
- if (txt_end < now) {
- GST_DEBUG ("Text buffer too old, popping off the queue");
- gst_text_overlay_pop_text (overlay);
- ret = GST_FLOW_OK;
- goto done;
- }
-
- /* if the video frame ends before the text even starts,
- * just push it out as is and pop it off the queue */
- if (frame_end < GST_BUFFER_TIMESTAMP (text_buf)) {
- GST_DEBUG ("Video buffer before text, pushing out and popping off queue");
- ret = gst_pad_push (overlay->srcpad, video_frame);
- gst_text_overlay_pop_video (overlay);
- video_frame = NULL;
- goto done;
- }
-
- /* text duration overlaps video frame duration */
- text = g_strndup ((gchar *) GST_BUFFER_DATA (text_buf),
- GST_BUFFER_SIZE (text_buf));
- g_strdelimit (text, "\n\r\t", ' ');
- text_len = strlen (text);
-
- if (text_len > 0) {
- GST_DEBUG ("Rendering text '%*s'", text_len, text);;
- gst_text_overlay_render_text (overlay, text, text_len);
- } else {
- GST_DEBUG ("No text to render (empty buffer)");
- gst_text_overlay_render_text (overlay, " ", 1);
- }
-
- g_free (text);
-
- gst_text_overlay_pop_video (overlay);
- ret = gst_text_overlay_push_frame (overlay, video_frame);
- video_frame = NULL;
- goto done;
-
-done:
- {
- if (text_buf)
- gst_buffer_unref (text_buf);
-
- if (video_frame)
- gst_buffer_unref (video_frame);
-
- return ret;
- }
-}
-
-static gboolean
-gst_text_overlay_src_event (GstPad * pad, GstEvent * event)
-{
- GstCairoTextOverlay *overlay =
- GST_CAIRO_TEXT_OVERLAY (gst_pad_get_parent (pad));
- gboolean ret = TRUE;
-
- /* forward events to the video sink, and, if it is linked, the text sink */
- if (overlay->text_collect_data) {
- gst_event_ref (event);
- ret &= gst_pad_push_event (overlay->text_sinkpad, event);
- }
- ret &= gst_pad_push_event (overlay->video_sinkpad, event);
-
- gst_object_unref (overlay);
- return ret;
-}
-
-static gboolean
-gst_text_overlay_video_event (GstPad * pad, GstEvent * event)
-{
- gboolean ret = FALSE;
- GstCairoTextOverlay *overlay = NULL;
-
- overlay = GST_CAIRO_TEXT_OVERLAY (gst_pad_get_parent (pad));
-
- if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
- GST_DEBUG_OBJECT (overlay,
- "received new segment on video sink pad, forwarding");
- gst_event_ref (event);
- gst_pad_push_event (overlay->srcpad, event);
- }
-
- /* now GstCollectPads can take care of the rest, e.g. EOS */
- ret = overlay->collect_event (pad, event);
- gst_object_unref (overlay);
- return ret;
-}
-
-static GstStateChangeReturn
-gst_text_overlay_change_state (GstElement * element, GstStateChange transition)
-{
- GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
- GstCairoTextOverlay *overlay = GST_CAIRO_TEXT_OVERLAY (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- gst_collect_pads_start (overlay->collect);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- /* need to unblock the collectpads before calling the
- * parent change_state so that streaming can finish */
- gst_collect_pads_stop (overlay->collect);
- break;
- default:
- break;
- }
-
- ret = parent_class->change_state (element, transition);
- if (ret == GST_STATE_CHANGE_FAILURE)
- return ret;
-
- switch (transition) {
- default:
- break;
- }
-
- return ret;
-}
+++ /dev/null
-/* 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., 51 Franklin St, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-/**
- * SECTION:element-cairotimeoverlay
- *
- * cairotimeoverlay renders the buffer timestamp for each frame on top of
- * the frame.
- *
- * <refsect2>
- * <title>Example launch line</title>
- * |[
- * gst-launch-1.0 videotestsrc ! cairotimeoverlay ! autovideosink
- * ]|
- * </refsect2>
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gst/math-compat.h>
-
-#include <gsttimeoverlay.h>
-
-#include <string.h>
-
-#include <cairo.h>
-
-#include <gst/video/video.h>
-
-static GstStaticPadTemplate gst_cairo_time_overlay_src_template =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
- );
-
-static GstStaticPadTemplate gst_cairo_time_overlay_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
- );
-
-static GstBaseTransformClass *parent_class = NULL;
-
-static void
-gst_cairo_time_overlay_update_font_height (GstCairoTimeOverlay * timeoverlay)
-{
- gint width, height;
- cairo_surface_t *font_surface;
- cairo_t *font_cairo;
- cairo_font_extents_t font_extents;
-
- width = timeoverlay->width;
- height = timeoverlay->height;
-
- font_surface =
- cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
- font_cairo = cairo_create (font_surface);
- cairo_surface_destroy (font_surface);
- font_surface = NULL;
-
- cairo_select_font_face (font_cairo, "monospace", 0, 0);
- cairo_set_font_size (font_cairo, 20);
- cairo_font_extents (font_cairo, &font_extents);
- timeoverlay->text_height = font_extents.height;
- GST_DEBUG_OBJECT (timeoverlay, "font height is %f", font_extents.height);
- cairo_destroy (font_cairo);
- font_cairo = NULL;
-}
-
-static gboolean
-gst_cairo_time_overlay_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
- GstCaps * outcaps)
-{
- GstCairoTimeOverlay *filter = GST_CAIRO_TIME_OVERLAY (btrans);
- GstStructure *structure;
- gboolean ret = FALSE;
-
- structure = gst_caps_get_structure (incaps, 0);
-
- if (gst_structure_get_int (structure, "width", &filter->width) &&
- gst_structure_get_int (structure, "height", &filter->height)) {
- gst_cairo_time_overlay_update_font_height (filter);
- ret = TRUE;
- }
-
- return ret;
-}
-
-/* Useful macros */
-#define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
-#define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
-#define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
-
-#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
-#define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
-#define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
-
-#define GST_VIDEO_I420_SIZE(w,h) (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
-
-static gboolean
-gst_cairo_time_overlay_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
- guint * size)
-{
- GstCairoTimeOverlay *filter;
- GstStructure *structure;
- gboolean ret = FALSE;
- gint width, height;
-
- filter = GST_CAIRO_TIME_OVERLAY (btrans);
-
- structure = gst_caps_get_structure (caps, 0);
-
- if (gst_structure_get_int (structure, "width", &width) &&
- gst_structure_get_int (structure, "height", &height)) {
- *size = GST_VIDEO_I420_SIZE (width, height);
- ret = TRUE;
- GST_DEBUG_OBJECT (filter, "our frame size is %d bytes (%dx%d)", *size,
- width, height);
- }
-
- return ret;
-}
-
-static char *
-gst_cairo_time_overlay_print_smpte_time (guint64 time)
-{
- int hours;
- int minutes;
- int seconds;
- int ms;
- double x;
-
- x = rint (gst_util_guint64_to_gdouble (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 GstFlowReturn
-gst_cairo_time_overlay_transform (GstBaseTransform * trans, GstBuffer * in,
- GstBuffer * out)
-{
- GstCairoTimeOverlay *timeoverlay;
- int width;
- int height;
- int b_width;
- int stride_y, stride_u, stride_v;
- char *string;
- int i, j;
- unsigned char *image;
- cairo_text_extents_t extents;
- guint8 *dest, *src;
- cairo_surface_t *font_surface;
- cairo_t *text_cairo;
- GstFlowReturn ret = GST_FLOW_OK;
-
- timeoverlay = GST_CAIRO_TIME_OVERLAY (trans);
-
- gst_buffer_copy_metadata (out, in, GST_BUFFER_COPY_TIMESTAMPS);
-
- src = GST_BUFFER_DATA (in);
- dest = GST_BUFFER_DATA (out);
-
- width = timeoverlay->width;
- height = timeoverlay->height;
-
- /* create surface for font rendering */
- /* FIXME: preparation of the surface could also be done once when settings
- * change */
- image = g_malloc (4 * width * timeoverlay->text_height);
-
- font_surface =
- cairo_image_surface_create_for_data (image, CAIRO_FORMAT_ARGB32, width,
- timeoverlay->text_height, width * 4);
- text_cairo = cairo_create (font_surface);
- cairo_surface_destroy (font_surface);
- font_surface = NULL;
-
- /* we draw a rectangle because the compositing on the buffer below
- * doesn't do alpha */
- cairo_save (text_cairo);
- cairo_rectangle (text_cairo, 0, 0, width, timeoverlay->text_height);
- cairo_set_source_rgba (text_cairo, 0, 0, 0, 1);
- cairo_set_operator (text_cairo, CAIRO_OPERATOR_SOURCE);
- cairo_fill (text_cairo);
- cairo_restore (text_cairo);
-
- string = gst_cairo_time_overlay_print_smpte_time (GST_BUFFER_TIMESTAMP (in));
- cairo_save (text_cairo);
- cairo_select_font_face (text_cairo, "monospace", 0, 0);
- cairo_set_font_size (text_cairo, 20);
- cairo_text_extents (text_cairo, string, &extents);
- cairo_set_source_rgb (text_cairo, 1, 1, 1);
- cairo_move_to (text_cairo, 0, timeoverlay->text_height - 2);
- cairo_show_text (text_cairo, string);
- g_free (string);
-
- cairo_restore (text_cairo);
-
- /* blend width; should retain a max text width so it doesn't jitter */
- b_width = extents.width;
- if (b_width > width)
- b_width = width;
-
- stride_y = GST_VIDEO_I420_Y_ROWSTRIDE (width);
- stride_u = GST_VIDEO_I420_U_ROWSTRIDE (width);
- stride_v = GST_VIDEO_I420_V_ROWSTRIDE (width);
-
- memcpy (dest, src, GST_BUFFER_SIZE (in));
- for (i = 0; i < timeoverlay->text_height; i++) {
- for (j = 0; j < b_width; j++) {
- ((unsigned char *) dest)[i * stride_y + j] =
- image[(i * width + j) * 4 + 0];
- }
- }
- for (i = 0; i < timeoverlay->text_height / 2; i++) {
- memset (dest + GST_VIDEO_I420_U_OFFSET (width, height) + i * stride_u, 128,
- b_width / 2);
- memset (dest + GST_VIDEO_I420_V_OFFSET (width, height) + i * stride_v, 128,
- b_width / 2);
- }
-
- cairo_destroy (text_cairo);
- text_cairo = NULL;
- g_free (image);
-
- return ret;
-}
-
-static void
-gst_cairo_time_overlay_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_set_static_metadata (element_class, "Time overlay",
- "Filter/Editor/Video",
- "Overlays the time on a video stream", "David Schleef <ds@schleef.org>");
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_cairo_time_overlay_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_cairo_time_overlay_src_template));
-}
-
-static void
-gst_cairo_time_overlay_class_init (gpointer klass, gpointer class_data)
-{
- GstBaseTransformClass *trans_class;
-
- trans_class = (GstBaseTransformClass *) klass;
-
- parent_class = g_type_class_peek_parent (klass);
-
- trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_cairo_time_overlay_set_caps);
- trans_class->get_unit_size =
- GST_DEBUG_FUNCPTR (gst_cairo_time_overlay_get_unit_size);
- trans_class->transform = GST_DEBUG_FUNCPTR (gst_cairo_time_overlay_transform);
-}
-
-static void
-gst_cairo_time_overlay_init (GTypeInstance * instance, gpointer g_class)
-{
-}
-
-GType
-gst_cairo_time_overlay_get_type (void)
-{
- static GType cairo_time_overlay_type = 0;
-
- if (!cairo_time_overlay_type) {
- static const GTypeInfo cairo_time_overlay_info = {
- sizeof (GstCairoTimeOverlayClass),
- gst_cairo_time_overlay_base_init,
- NULL,
- gst_cairo_time_overlay_class_init,
- NULL,
- NULL,
- sizeof (GstCairoTimeOverlay),
- 0,
- gst_cairo_time_overlay_init,
- };
-
- cairo_time_overlay_type = g_type_register_static (GST_TYPE_BASE_TRANSFORM,
- "GstCairoTimeOverlay", &cairo_time_overlay_info, 0);
- }
- return cairo_time_overlay_type;
-}