VideoFilter inherits from
[platform/upstream/gstreamer.git] / ext / cairo / gsttimeoverlay.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2003> David Schleef <ds@schleef.org>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /*
22  * This file was (probably) generated from gsttimeoverlay.c,
23  * gsttimeoverlay.c,v 1.7 2003/11/08 02:48:59 dschleef Exp 
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <gsttimeoverlay.h>
31
32 #include <string.h>
33 #include <math.h>
34
35 #include <cairo.h>
36
37 #include <gst/video/video.h>
38
39 static GstElementDetails timeoverlay_details =
40 GST_ELEMENT_DETAILS ("Time Overlay",
41     "Filter/Editor/Video",
42     "Overlays the time on a video stream",
43     "David Schleef <ds@schleef.org>");
44
45 static GstStaticPadTemplate gst_timeoverlay_src_template =
46 GST_STATIC_PAD_TEMPLATE ("src",
47     GST_PAD_SRC,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
50     );
51
52 static GstStaticPadTemplate gst_timeoverlay_sink_template =
53 GST_STATIC_PAD_TEMPLATE ("sink",
54     GST_PAD_SINK,
55     GST_PAD_ALWAYS,
56     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
57     );
58
59 static GstVideofilterClass *parent_class = NULL;
60
61 static void
62 gst_timeoverlay_update_font_height (GstTimeoverlay * timeoverlay)
63 {
64   gint width, height;
65   cairo_surface_t *font_surface;
66   cairo_t *font_cairo;
67   cairo_font_extents_t font_extents;
68
69   width = timeoverlay->width;
70   height = timeoverlay->height;
71
72   font_surface =
73       cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
74   font_cairo = cairo_create (font_surface);
75   cairo_surface_destroy (font_surface);
76   font_surface = NULL;
77
78   cairo_select_font_face (font_cairo, "monospace", 0, 0);
79   cairo_set_font_size (font_cairo, 20);
80   cairo_font_extents (font_cairo, &font_extents);
81   timeoverlay->text_height = font_extents.height;
82   GST_DEBUG_OBJECT (timeoverlay, "font height is %d", font_extents.height);
83   cairo_destroy (font_cairo);
84   font_cairo = NULL;
85 }
86
87 static gboolean
88 gst_timeoverlay_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
89     GstCaps * outcaps)
90 {
91   GstTimeoverlay *filter = GST_TIMEOVERLAY (btrans);
92   GstStructure *structure;
93   gboolean ret = FALSE;
94
95   structure = gst_caps_get_structure (incaps, 0);
96
97   if (gst_structure_get_int (structure, "width", &filter->width) &&
98       gst_structure_get_int (structure, "height", &filter->height)) {
99     gst_timeoverlay_update_font_height (filter);
100     ret = TRUE;
101   }
102
103   return ret;
104 }
105
106 /* Useful macros */
107 #define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
108 #define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
109 #define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
110
111 #define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
112 #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)))
113 #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))
114
115 #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))
116
117 static gboolean
118 gst_timeoverlay_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
119     guint * size)
120 {
121   GstTimeoverlay *filter;
122   GstStructure *structure;
123   gboolean ret = FALSE;
124   gint width, height;
125
126   filter = GST_TIMEOVERLAY (btrans);
127
128   structure = gst_caps_get_structure (caps, 0);
129
130   if (gst_structure_get_int (structure, "width", &width) &&
131       gst_structure_get_int (structure, "height", &height)) {
132     *size = GST_VIDEO_I420_SIZE (width, height);
133     ret = TRUE;
134     GST_DEBUG_OBJECT (filter, "our frame size is %d bytes (%dx%d)", *size,
135         width, height);
136   }
137
138   return ret;
139 }
140
141 static char *
142 gst_timeoverlay_print_smpte_time (guint64 time)
143 {
144   int hours;
145   int minutes;
146   int seconds;
147   int ms;
148   double x;
149
150   x = rint ((time + 500000) * 1e-6);
151
152   hours = floor (x / (60 * 60 * 1000));
153   x -= hours * 60 * 60 * 1000;
154   minutes = floor (x / (60 * 1000));
155   x -= minutes * 60 * 1000;
156   seconds = floor (x / (1000));
157   x -= seconds * 1000;
158   ms = rint (x);
159
160   return g_strdup_printf ("%02d:%02d:%02d.%03d", hours, minutes, seconds, ms);
161 }
162
163
164 static GstFlowReturn
165 gst_timeoverlay_transform (GstBaseTransform * trans, GstBuffer * in,
166     GstBuffer * out)
167 {
168   GstTimeoverlay *timeoverlay;
169   int width;
170   int height;
171   int b_width;
172   char *string;
173   int i, j;
174   unsigned char *image;
175   cairo_text_extents_t extents;
176   gpointer dest, src;
177   cairo_surface_t *font_surface;
178   cairo_t *text_cairo;
179   GstFlowReturn ret = GST_FLOW_OK;
180
181   timeoverlay = GST_TIMEOVERLAY (trans);
182
183   gst_buffer_stamp (out, in);
184
185   src = GST_BUFFER_DATA (in);
186   dest = GST_BUFFER_DATA (out);
187
188   width = timeoverlay->width;
189   height = timeoverlay->height;
190
191   /* create surface for font rendering */
192   /* FIXME: preparation of the surface could also be done once when settings
193    * change */
194   image = g_malloc (4 * width * timeoverlay->text_height);
195
196   font_surface =
197       cairo_image_surface_create_for_data (image, CAIRO_FORMAT_ARGB32, width,
198       timeoverlay->text_height, width * 4);
199   text_cairo = cairo_create (font_surface);
200   cairo_surface_destroy (font_surface);
201   font_surface = NULL;
202
203   /* we draw a rectangle because the compositing on the buffer below
204    * doesn't do alpha */
205   cairo_save (text_cairo);
206   cairo_rectangle (text_cairo, 0, 0, width, timeoverlay->text_height);
207   cairo_set_source_rgba (text_cairo, 0, 0, 0, 1);
208   cairo_set_operator (text_cairo, CAIRO_OPERATOR_SOURCE);
209   cairo_fill (text_cairo);
210   cairo_restore (text_cairo);
211
212   string = gst_timeoverlay_print_smpte_time (GST_BUFFER_TIMESTAMP (in));
213   cairo_save (text_cairo);
214   cairo_select_font_face (text_cairo, "monospace", 0, 0);
215   cairo_set_font_size (text_cairo, 20);
216   cairo_text_extents (text_cairo, string, &extents);
217   cairo_set_source_rgb (text_cairo, 1, 1, 1);
218   cairo_move_to (text_cairo, 0, timeoverlay->text_height - 2);
219   cairo_show_text (text_cairo, string);
220   g_free (string);
221
222   cairo_restore (text_cairo);
223
224   /* blend width; should retain a max text width so it doesn't jitter */
225   b_width = extents.width;
226   if (b_width > width)
227     b_width = width;
228
229   memcpy (dest, src, GST_BUFFER_SIZE (in));
230   for (i = 0; i < timeoverlay->text_height; i++) {
231     for (j = 0; j < b_width; j++) {
232       ((unsigned char *) dest)[i * width + j] = image[(i * width + j) * 4 + 0];
233     }
234   }
235   for (i = 0; i < timeoverlay->text_height / 2; i++) {
236     memset (dest + width * height + i * (width / 2), 128, b_width / 2);
237     memset (dest + width * height + (width / 2) * (height / 2) +
238         i * (width / 2), 128, b_width / 2);
239   }
240
241   cairo_destroy (text_cairo);
242   text_cairo = NULL;
243   g_free (image);
244
245   return ret;
246 }
247
248 static void
249 gst_timeoverlay_base_init (gpointer g_class)
250 {
251   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
252
253   gst_element_class_set_details (element_class, &timeoverlay_details);
254
255   gst_element_class_add_pad_template (element_class,
256       gst_static_pad_template_get (&gst_timeoverlay_sink_template));
257   gst_element_class_add_pad_template (element_class,
258       gst_static_pad_template_get (&gst_timeoverlay_src_template));
259 }
260
261 static void
262 gst_timeoverlay_class_init (gpointer klass, gpointer class_data)
263 {
264   GObjectClass *gobject_class;
265   GstElementClass *element_class;
266   GstBaseTransformClass *trans_class;
267
268   gobject_class = (GObjectClass *) klass;
269   element_class = (GstElementClass *) klass;
270   trans_class = (GstBaseTransformClass *) klass;
271
272   parent_class = g_type_class_peek_parent (klass);
273
274   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_timeoverlay_set_caps);
275   trans_class->get_unit_size =
276       GST_DEBUG_FUNCPTR (gst_timeoverlay_get_unit_size);
277   trans_class->transform = GST_DEBUG_FUNCPTR (gst_timeoverlay_transform);
278 }
279
280 static void
281 gst_timeoverlay_init (GTypeInstance * instance, gpointer g_class)
282 {
283 }
284
285 GType
286 gst_timeoverlay_get_type (void)
287 {
288   static GType timeoverlay_type = 0;
289
290   if (!timeoverlay_type) {
291     static const GTypeInfo timeoverlay_info = {
292       sizeof (GstTimeoverlayClass),
293       gst_timeoverlay_base_init,
294       NULL,
295       gst_timeoverlay_class_init,
296       NULL,
297       NULL,
298       sizeof (GstTimeoverlay),
299       0,
300       gst_timeoverlay_init,
301     };
302
303     timeoverlay_type = g_type_register_static (GST_TYPE_VIDEOFILTER,
304         "GstTimeoverlay", &timeoverlay_info, 0);
305   }
306   return timeoverlay_type;
307 }