2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2003> David Schleef <ds@schleef.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-cairotimeoverlay
24 * cairotimeoverlay renders the buffer timestamp for each frame on top of
28 * <title>Example launch line</title>
30 * gst-launch videotestsrc ! cairotimeoverlay ! autovideosink
39 #include <gsttimeoverlay.h>
46 #include <gst/video/video.h>
49 #define rint(x) ((double) floor((x)+(((x) < 0)? -0.5 : 0.5)))
52 static const GstElementDetails cairo_time_overlay_details =
53 GST_ELEMENT_DETAILS ("Time overlay",
54 "Filter/Editor/Video",
55 "Overlays the time on a video stream",
56 "David Schleef <ds@schleef.org>");
58 static GstStaticPadTemplate gst_cairo_time_overlay_src_template =
59 GST_STATIC_PAD_TEMPLATE ("src",
62 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
65 static GstStaticPadTemplate gst_cairo_time_overlay_sink_template =
66 GST_STATIC_PAD_TEMPLATE ("sink",
69 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
72 static GstBaseTransformClass *parent_class = NULL;
75 gst_cairo_time_overlay_update_font_height (GstCairoTimeOverlay * timeoverlay)
78 cairo_surface_t *font_surface;
80 cairo_font_extents_t font_extents;
82 width = timeoverlay->width;
83 height = timeoverlay->height;
86 cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
87 font_cairo = cairo_create (font_surface);
88 cairo_surface_destroy (font_surface);
91 cairo_select_font_face (font_cairo, "monospace", 0, 0);
92 cairo_set_font_size (font_cairo, 20);
93 cairo_font_extents (font_cairo, &font_extents);
94 timeoverlay->text_height = font_extents.height;
95 GST_DEBUG_OBJECT (timeoverlay, "font height is %f", font_extents.height);
96 cairo_destroy (font_cairo);
101 gst_cairo_time_overlay_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
104 GstCairoTimeOverlay *filter = GST_CAIRO_TIME_OVERLAY (btrans);
105 GstStructure *structure;
106 gboolean ret = FALSE;
108 structure = gst_caps_get_structure (incaps, 0);
110 if (gst_structure_get_int (structure, "width", &filter->width) &&
111 gst_structure_get_int (structure, "height", &filter->height)) {
112 gst_cairo_time_overlay_update_font_height (filter);
120 #define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
121 #define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
122 #define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
124 #define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
125 #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)))
126 #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))
128 #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))
131 gst_cairo_time_overlay_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
134 GstCairoTimeOverlay *filter;
135 GstStructure *structure;
136 gboolean ret = FALSE;
139 filter = GST_CAIRO_TIME_OVERLAY (btrans);
141 structure = gst_caps_get_structure (caps, 0);
143 if (gst_structure_get_int (structure, "width", &width) &&
144 gst_structure_get_int (structure, "height", &height)) {
145 *size = GST_VIDEO_I420_SIZE (width, height);
147 GST_DEBUG_OBJECT (filter, "our frame size is %d bytes (%dx%d)", *size,
155 gst_cairo_time_overlay_print_smpte_time (guint64 time)
163 x = rint (gst_util_guint64_to_gdouble (time + 500000) * 1e-6);
165 hours = floor (x / (60 * 60 * 1000));
166 x -= hours * 60 * 60 * 1000;
167 minutes = floor (x / (60 * 1000));
168 x -= minutes * 60 * 1000;
169 seconds = floor (x / (1000));
173 return g_strdup_printf ("%02d:%02d:%02d.%03d", hours, minutes, seconds, ms);
178 gst_cairo_time_overlay_transform (GstBaseTransform * trans, GstBuffer * in,
181 GstCairoTimeOverlay *timeoverlay;
185 int stride_y, stride_u, stride_v;
188 unsigned char *image;
189 cairo_text_extents_t extents;
191 cairo_surface_t *font_surface;
193 GstFlowReturn ret = GST_FLOW_OK;
195 timeoverlay = GST_CAIRO_TIME_OVERLAY (trans);
197 gst_buffer_copy_metadata (out, in, GST_BUFFER_COPY_TIMESTAMPS);
199 src = GST_BUFFER_DATA (in);
200 dest = GST_BUFFER_DATA (out);
202 width = timeoverlay->width;
203 height = timeoverlay->height;
205 /* create surface for font rendering */
206 /* FIXME: preparation of the surface could also be done once when settings
208 image = g_malloc (4 * width * timeoverlay->text_height);
211 cairo_image_surface_create_for_data (image, CAIRO_FORMAT_ARGB32, width,
212 timeoverlay->text_height, width * 4);
213 text_cairo = cairo_create (font_surface);
214 cairo_surface_destroy (font_surface);
217 /* we draw a rectangle because the compositing on the buffer below
218 * doesn't do alpha */
219 cairo_save (text_cairo);
220 cairo_rectangle (text_cairo, 0, 0, width, timeoverlay->text_height);
221 cairo_set_source_rgba (text_cairo, 0, 0, 0, 1);
222 cairo_set_operator (text_cairo, CAIRO_OPERATOR_SOURCE);
223 cairo_fill (text_cairo);
224 cairo_restore (text_cairo);
226 string = gst_cairo_time_overlay_print_smpte_time (GST_BUFFER_TIMESTAMP (in));
227 cairo_save (text_cairo);
228 cairo_select_font_face (text_cairo, "monospace", 0, 0);
229 cairo_set_font_size (text_cairo, 20);
230 cairo_text_extents (text_cairo, string, &extents);
231 cairo_set_source_rgb (text_cairo, 1, 1, 1);
232 cairo_move_to (text_cairo, 0, timeoverlay->text_height - 2);
233 cairo_show_text (text_cairo, string);
236 cairo_restore (text_cairo);
238 /* blend width; should retain a max text width so it doesn't jitter */
239 b_width = extents.width;
243 stride_y = GST_VIDEO_I420_Y_ROWSTRIDE (width);
244 stride_u = GST_VIDEO_I420_U_ROWSTRIDE (width);
245 stride_v = GST_VIDEO_I420_V_ROWSTRIDE (width);
247 memcpy (dest, src, GST_BUFFER_SIZE (in));
248 for (i = 0; i < timeoverlay->text_height; i++) {
249 for (j = 0; j < b_width; j++) {
250 ((unsigned char *) dest)[i * stride_y + j] =
251 image[(i * width + j) * 4 + 0];
254 for (i = 0; i < timeoverlay->text_height / 2; i++) {
255 memset (dest + GST_VIDEO_I420_U_OFFSET (width, height) + i * stride_u, 128,
257 memset (dest + GST_VIDEO_I420_V_OFFSET (width, height) + i * stride_v, 128,
261 cairo_destroy (text_cairo);
269 gst_cairo_time_overlay_base_init (gpointer g_class)
271 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
273 gst_element_class_set_details (element_class, &cairo_time_overlay_details);
275 gst_element_class_add_pad_template (element_class,
276 gst_static_pad_template_get (&gst_cairo_time_overlay_sink_template));
277 gst_element_class_add_pad_template (element_class,
278 gst_static_pad_template_get (&gst_cairo_time_overlay_src_template));
282 gst_cairo_time_overlay_class_init (gpointer klass, gpointer class_data)
284 GstBaseTransformClass *trans_class;
286 trans_class = (GstBaseTransformClass *) klass;
288 parent_class = g_type_class_peek_parent (klass);
290 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_cairo_time_overlay_set_caps);
291 trans_class->get_unit_size =
292 GST_DEBUG_FUNCPTR (gst_cairo_time_overlay_get_unit_size);
293 trans_class->transform = GST_DEBUG_FUNCPTR (gst_cairo_time_overlay_transform);
297 gst_cairo_time_overlay_init (GTypeInstance * instance, gpointer g_class)
302 gst_cairo_time_overlay_get_type (void)
304 static GType cairo_time_overlay_type = 0;
306 if (!cairo_time_overlay_type) {
307 static const GTypeInfo cairo_time_overlay_info = {
308 sizeof (GstCairoTimeOverlayClass),
309 gst_cairo_time_overlay_base_init,
311 gst_cairo_time_overlay_class_init,
314 sizeof (GstCairoTimeOverlay),
316 gst_cairo_time_overlay_init,
319 cairo_time_overlay_type = g_type_register_static (GST_TYPE_BASE_TRANSFORM,
320 "GstCairoTimeOverlay", &cairo_time_overlay_info, 0);
322 return cairo_time_overlay_type;