gst-indent
[platform/upstream/gst-plugins-good.git] / ext / pango / gsttextoverlay.c
1
2
3
4 #ifdef HAVE_CONFIG_H
5 #include <config.h>
6 #endif
7 #include <gst/gst.h>
8 #include "gsttextoverlay.h"
9 /*#include "gsttexttestsrc.h"*/
10 /*#include "gstsubparse.h"*/
11 /*#include "SDL_blit.h"*/
12
13 static GstElementDetails textoverlay_details = {
14   "Text Overlay",
15   "Filter/Editor/Video",
16   "Adds text strings on top of a video buffer",
17   "Gustavo J. A. M. Carneiro <gjc@inescporto.pt>"
18 };
19
20 enum
21 {
22   ARG_0,
23   ARG_TEXT,
24   ARG_VALIGN,
25   ARG_HALIGN,
26   ARG_X0,
27   ARG_Y0,
28   ARG_FONT_DESC,
29 };
30
31
32 static GstStaticPadTemplate textoverlay_src_template_factory =
33 GST_STATIC_PAD_TEMPLATE ("src",
34     GST_PAD_SRC,
35     GST_PAD_ALWAYS,
36     GST_STATIC_CAPS ("video/x-raw-yuv, "
37         "format = (fourcc) I420, "
38         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
39     );
40
41 static GstStaticPadTemplate video_sink_template_factory =
42 GST_STATIC_PAD_TEMPLATE ("video_sink",
43     GST_PAD_SINK,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS ("video/x-raw-yuv, "
46         "format = (fourcc) I420, "
47         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
48     );
49
50 static GstStaticPadTemplate text_sink_template_factory =
51     GST_STATIC_PAD_TEMPLATE ("text_sink",
52     GST_PAD_SINK,
53     GST_PAD_ALWAYS,
54     GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
55     );
56
57 static void gst_textoverlay_base_init (gpointer g_class);
58 static void gst_textoverlay_class_init (GstTextOverlayClass * klass);
59 static void gst_textoverlay_init (GstTextOverlay * overlay);
60 static void gst_textoverlay_set_property (GObject * object,
61     guint prop_id, const GValue * value, GParamSpec * pspec);
62 static void gst_textoverlay_get_property (GObject * object,
63     guint prop_id, GValue * value, GParamSpec * pspec);
64 static GstElementStateReturn gst_textoverlay_change_state (GstElement *
65     element);
66 static void gst_textoverlay_finalize (GObject * object);
67
68
69 static GstElementClass *parent_class = NULL;
70
71 /*static guint gst_textoverlay_signals[LAST_SIGNAL] = { 0 }; */
72
73
74 GType
75 gst_textoverlay_get_type (void)
76 {
77   static GType textoverlay_type = 0;
78
79   if (!textoverlay_type) {
80     static const GTypeInfo textoverlay_info = {
81       sizeof (GstTextOverlayClass),
82       gst_textoverlay_base_init,
83       NULL,
84       (GClassInitFunc) gst_textoverlay_class_init,
85       NULL,
86       NULL,
87       sizeof (GstTextOverlay),
88       0,
89       (GInstanceInitFunc) gst_textoverlay_init,
90     };
91     textoverlay_type =
92         g_type_register_static (GST_TYPE_ELEMENT, "GstTextOverlay",
93         &textoverlay_info, 0);
94   }
95   return textoverlay_type;
96 }
97
98 static void
99 gst_textoverlay_base_init (gpointer g_class)
100 {
101   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
102
103   gst_element_class_add_pad_template (element_class,
104       gst_static_pad_template_get (&textoverlay_src_template_factory));
105   gst_element_class_add_pad_template (element_class,
106       gst_static_pad_template_get (&video_sink_template_factory));
107   gst_element_class_add_pad_template (element_class,
108       gst_static_pad_template_get (&text_sink_template_factory));
109
110   gst_element_class_set_details (element_class, &textoverlay_details);
111 }
112
113 static void
114 gst_textoverlay_class_init (GstTextOverlayClass * klass)
115 {
116   GObjectClass *gobject_class;
117   GstElementClass *gstelement_class;
118
119   gobject_class = (GObjectClass *) klass;
120   gstelement_class = (GstElementClass *) klass;
121
122   parent_class = g_type_class_peek_parent (klass);
123
124   gobject_class->finalize = gst_textoverlay_finalize;
125   gobject_class->set_property = gst_textoverlay_set_property;
126   gobject_class->get_property = gst_textoverlay_get_property;
127
128   gstelement_class->change_state = gst_textoverlay_change_state;
129   klass->pango_context = pango_ft2_get_context (72, 72);
130   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TEXT,
131       g_param_spec_string ("text", "text",
132           "Text to be display,"
133           " in pango markup format.", "", G_PARAM_WRITABLE));
134   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_VALIGN,
135       g_param_spec_string ("valign", "vertical alignment",
136           "Vertical alignment of the text. "
137           "Can be either 'baseline', 'bottom', or 'top'",
138           "baseline", G_PARAM_WRITABLE));
139   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HALIGN,
140       g_param_spec_string ("halign", "horizontal alignment",
141           "Horizontal alignment of the text. "
142           "Can be either 'left', 'right', or 'center'",
143           "center", G_PARAM_WRITABLE));
144   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_X0,
145       g_param_spec_int ("x0", "X position",
146           "Initial X position."
147           " Horizontal aligment takes this point"
148           " as reference.", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
149   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_Y0,
150       g_param_spec_int ("y0", "Y position",
151           "Initial Y position."
152           " Vertical aligment takes this point"
153           " as reference.", G_MININT, G_MAXINT, 0, G_PARAM_WRITABLE));
154   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC,
155       g_param_spec_string ("font-desc", "font description",
156           "Pango font description of font "
157           "to be used for rendering. "
158           "See documentation of "
159           "pango_font_description_from_string"
160           " for syntax.", "", G_PARAM_WRITABLE));
161 }
162
163
164 static void
165 resize_bitmap (GstTextOverlay * overlay, int width, int height)
166 {
167   FT_Bitmap *bitmap = &overlay->bitmap;
168   int pitch = (width | 3) + 1;
169   int size = pitch * height;
170
171   /* no need to keep reallocating; just keep the maximum size so far */
172   if (size <= overlay->bitmap_buffer_size) {
173     bitmap->rows = height;
174     bitmap->width = width;
175     bitmap->pitch = pitch;
176     memset (bitmap->buffer, 0, overlay->bitmap_buffer_size);
177     return;
178   }
179   if (!bitmap->buffer) {
180     /* initialize */
181     bitmap->pixel_mode = ft_pixel_mode_grays;
182     bitmap->num_grays = 256;
183   }
184   if (bitmap->buffer)
185     bitmap->buffer = g_realloc (bitmap->buffer, size);
186   else
187     bitmap->buffer = g_malloc (size);
188   bitmap->rows = height;
189   bitmap->width = width;
190   bitmap->pitch = pitch;
191   memset (bitmap->buffer, 0, size);
192   overlay->bitmap_buffer_size = size;
193 }
194
195 static void
196 render_text (GstTextOverlay * overlay)
197 {
198   PangoRectangle ink_rect, logical_rect;
199
200   pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
201   resize_bitmap (overlay, ink_rect.width, ink_rect.height + ink_rect.y);
202   pango_ft2_render_layout (&overlay->bitmap, overlay->layout, 0, 0);
203   overlay->baseline_y = ink_rect.y;
204 }
205
206 /* static GstPadLinkReturn */
207 /* gst_textoverlay_text_sinkconnect (GstPad *pad, GstCaps *caps) */
208 /* { */
209 /*     return GST_PAD_LINK_DONE; */
210 /* } */
211
212
213 static GstPadLinkReturn
214 gst_textoverlay_video_sinkconnect (GstPad * pad, const GstCaps * caps)
215 {
216   GstTextOverlay *overlay;
217   GstStructure *structure;
218
219   overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
220
221   structure = gst_caps_get_structure (caps, 0);
222   overlay->width = overlay->height = 0;
223   gst_structure_get_int (structure, "width", &overlay->width);
224   gst_structure_get_int (structure, "height", &overlay->height);
225
226   return gst_pad_try_set_caps (overlay->srcpad, caps);
227 }
228
229
230 static void
231 gst_text_overlay_blit_yuv420 (GstTextOverlay * overlay, FT_Bitmap * bitmap,
232     guchar * pixbuf, int x0, int y0)
233 {
234   int y;                        /* text bitmap coordinates */
235   int x1, y1;                   /* video buffer coordinates */
236   int rowinc, bit_rowinc, uv_rowinc;
237   guchar *p, *bitp, *u_p;
238   int video_width = overlay->width, video_height = overlay->height;
239   int bitmap_x0 = x0 < 1 ? -(x0 - 1) : 1;       /* 1 pixel border */
240   int bitmap_y0 = y0 < 1 ? -(y0 - 1) : 1;       /* 1 pixel border */
241   int bitmap_width = bitmap->width - bitmap_x0;
242   int bitmap_height = bitmap->rows - bitmap_y0;
243   int u_plane_size;
244   int skip_y, skip_x;
245   guchar v;
246
247   if (x0 + bitmap_x0 + bitmap_width > video_width - 1)  /* 1 pixel border */
248     bitmap_width -= x0 + bitmap_x0 + bitmap_width - video_width + 1;
249   if (y0 + bitmap_y0 + bitmap_height > video_height - 1)        /* 1 pixel border */
250     bitmap_height -= y0 + bitmap_y0 + bitmap_height - video_height + 1;
251
252   rowinc = video_width - bitmap_width;
253   uv_rowinc = video_width / 2 - bitmap_width / 2;
254   bit_rowinc = bitmap->pitch - bitmap_width;
255   u_plane_size = (video_width / 2) * (video_height / 2);
256
257   y1 = y0 + bitmap_y0;
258   x1 = x0 + bitmap_x0;
259   p = pixbuf + video_width * y1 + x1;
260   bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
261   for (y = bitmap_y0; y < bitmap_height; y++) {
262     int n;
263
264     for (n = bitmap_width; n > 0; --n) {
265       v = *bitp;
266       if (v) {
267         p[-1] = CLAMP (p[-1] - v, 0, 255);
268         p[1] = CLAMP (p[1] - v, 0, 255);
269         p[-video_width] = CLAMP (p[-video_width] - v, 0, 255);
270         p[video_width] = CLAMP (p[video_width] - v, 0, 255);
271       }
272       p++;
273       bitp++;
274     }
275     p += rowinc;
276     bitp += bit_rowinc;
277   }
278
279   y = bitmap_y0;
280   y1 = y0 + bitmap_y0;
281   x1 = x0 + bitmap_x0;
282   bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
283   p = pixbuf + video_width * y1 + x1;
284   u_p =
285       pixbuf + video_width * video_height + (video_width >> 1) * (y1 >> 1) +
286       (x1 >> 1);
287   skip_y = 0;
288   skip_x = 0;
289
290   for (; y < bitmap_height; y++) {
291     int n;
292
293     x1 = x0 + bitmap_x0;
294     skip_x = 0;
295     for (n = bitmap_width; n > 0; --n) {
296       v = *bitp;
297       if (v) {
298         *p = v;
299         if (!skip_y) {
300           u_p[0] = u_p[u_plane_size] = 0x80;
301         }
302       }
303       if (!skip_y) {
304         skip_x = !skip_x;
305         if (!skip_x)
306           u_p++;
307       }
308       p++;
309       bitp++;
310     }
311     /*if (!skip_x && !skip_y) u_p--; */
312     p += rowinc;
313     bitp += bit_rowinc;
314     skip_y = !skip_y;
315     u_p += skip_y ? uv_rowinc : 0;
316   }
317 }
318
319
320 static void
321 gst_textoverlay_video_chain (GstPad * pad, GstData * _data)
322 {
323   GstBuffer *buf = GST_BUFFER (_data);
324   GstTextOverlay *overlay;
325   guchar *pixbuf;
326   gint x0, y0;
327
328   g_return_if_fail (pad != NULL);
329   g_return_if_fail (GST_IS_PAD (pad));
330   g_return_if_fail (buf != NULL);
331   overlay = GST_TEXTOVERLAY (gst_pad_get_parent (pad));
332   g_return_if_fail (overlay != NULL);
333   g_return_if_fail (GST_IS_TEXTOVERLAY (overlay));
334
335   pixbuf = GST_BUFFER_DATA (buf);
336
337   x0 = overlay->x0;
338   y0 = overlay->y0;
339   switch (overlay->valign) {
340     case GST_TEXT_OVERLAY_VALIGN_BOTTOM:
341       y0 += overlay->bitmap.rows;
342       break;
343     case GST_TEXT_OVERLAY_VALIGN_BASELINE:
344       y0 -= (overlay->bitmap.rows - overlay->baseline_y);
345       break;
346     case GST_TEXT_OVERLAY_VALIGN_TOP:
347       break;
348   }
349
350   switch (overlay->halign) {
351     case GST_TEXT_OVERLAY_HALIGN_LEFT:
352       break;
353     case GST_TEXT_OVERLAY_HALIGN_RIGHT:
354       x0 -= overlay->bitmap.width;
355       break;
356     case GST_TEXT_OVERLAY_HALIGN_CENTER:
357       x0 -= overlay->bitmap.width / 2;
358       break;
359   }
360
361   if (overlay->bitmap.buffer)
362     gst_text_overlay_blit_yuv420 (overlay, &overlay->bitmap, pixbuf, x0, y0);
363
364   gst_pad_push (overlay->srcpad, GST_DATA (buf));
365 }
366
367 #define PAST_END(buffer, time) \
368   (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE && \
369    GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE && \
370    GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) \
371      < (time))
372
373 static void
374 gst_textoverlay_loop (GstElement * element)
375 {
376   GstTextOverlay *overlay;
377   GstBuffer *video_frame;
378   guint64 now;
379
380   g_return_if_fail (element != NULL);
381   g_return_if_fail (GST_IS_TEXTOVERLAY (element));
382   overlay = GST_TEXTOVERLAY (element);
383
384   video_frame = GST_BUFFER (gst_pad_pull (overlay->video_sinkpad));
385   now = GST_BUFFER_TIMESTAMP (video_frame);
386
387   /*
388    * This state machine has a bug that can't be resolved easily.
389    * (Needs a more complicated state machine.)  Basically, if the
390    * text that came from a buffer from the sink pad is being
391    * displayed, and the default text is changed by set_parameter,
392    * we'll incorrectly display the default text.
393    *
394    * Otherwise, this is a pretty decent state machine that handles
395    * buffer timestamps and durations correctly.  (I think)
396    */
397
398   while (overlay->next_buffer == NULL) {
399     GST_DEBUG ("attempting to pull a buffer");
400
401     /* read all text buffers until we get one "in the future" */
402     if (!GST_PAD_IS_USABLE (overlay->text_sinkpad)) {
403       break;
404     }
405     overlay->next_buffer = GST_BUFFER (gst_pad_pull (overlay->text_sinkpad));
406     if (!overlay->next_buffer)
407       break;
408
409     if (PAST_END (overlay->next_buffer, now)) {
410       gst_buffer_unref (overlay->next_buffer);
411       overlay->next_buffer = NULL;
412     }
413   }
414
415   if (overlay->next_buffer &&
416       (GST_BUFFER_TIMESTAMP (overlay->next_buffer) <= now ||
417           GST_BUFFER_TIMESTAMP (overlay->next_buffer) == GST_CLOCK_TIME_NONE)) {
418     GST_DEBUG ("using new buffer");
419
420     if (overlay->current_buffer) {
421       gst_buffer_unref (overlay->current_buffer);
422     }
423     overlay->current_buffer = overlay->next_buffer;
424     overlay->next_buffer = NULL;
425
426     GST_DEBUG ("rendering '%*s'",
427         GST_BUFFER_SIZE (overlay->current_buffer),
428         GST_BUFFER_DATA (overlay->current_buffer));
429     pango_layout_set_markup (overlay->layout,
430         GST_BUFFER_DATA (overlay->current_buffer),
431         GST_BUFFER_SIZE (overlay->current_buffer));
432     render_text (overlay);
433     overlay->need_render = FALSE;
434   }
435
436   if (overlay->current_buffer && PAST_END (overlay->current_buffer, now)) {
437     GST_DEBUG ("dropping old buffer");
438
439     gst_buffer_unref (overlay->current_buffer);
440     overlay->current_buffer = NULL;
441
442     overlay->need_render = TRUE;
443   }
444
445   if (overlay->need_render) {
446     GST_DEBUG ("rendering '%s'", overlay->default_text);
447     pango_layout_set_markup (overlay->layout,
448         overlay->default_text, strlen (overlay->default_text));
449     render_text (overlay);
450
451     overlay->need_render = FALSE;
452   }
453
454   gst_textoverlay_video_chain (overlay->srcpad, GST_DATA (video_frame));
455 }
456
457
458 static GstElementStateReturn
459 gst_textoverlay_change_state (GstElement * element)
460 {
461   GstTextOverlay *overlay;
462
463   overlay = GST_TEXTOVERLAY (element);
464
465   switch (GST_STATE_TRANSITION (element)) {
466     case GST_STATE_PAUSED_TO_PLAYING:
467       break;
468     case GST_STATE_PLAYING_TO_PAUSED:
469       break;
470     case GST_STATE_PAUSED_TO_READY:
471       break;
472   }
473
474   parent_class->change_state (element);
475
476   return GST_STATE_SUCCESS;
477 }
478
479 static void
480 gst_textoverlay_finalize (GObject * object)
481 {
482   GstTextOverlay *overlay = GST_TEXTOVERLAY (object);
483
484   if (overlay->layout) {
485     g_object_unref (overlay->layout);
486     overlay->layout = NULL;
487   }
488   if (overlay->bitmap.buffer) {
489     g_free (overlay->bitmap.buffer);
490     overlay->bitmap.buffer = NULL;
491   }
492
493   G_OBJECT_CLASS (parent_class)->finalize (object);
494 }
495
496 static void
497 gst_textoverlay_init (GstTextOverlay * overlay)
498 {
499   /* video sink */
500   overlay->video_sinkpad =
501       gst_pad_new_from_template (gst_static_pad_template_get
502       (&video_sink_template_factory), "video_sink");
503 /*     gst_pad_set_chain_function(overlay->video_sinkpad, gst_textoverlay_video_chain); */
504   gst_pad_set_link_function (overlay->video_sinkpad,
505       gst_textoverlay_video_sinkconnect);
506   gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
507
508   /* text sink */
509   overlay->text_sinkpad =
510       gst_pad_new_from_template (gst_static_pad_template_get
511       (&text_sink_template_factory), "text_sink");
512 /*     gst_pad_set_link_function(overlay->text_sinkpad, gst_textoverlay_text_sinkconnect); */
513   gst_element_add_pad (GST_ELEMENT (overlay), overlay->text_sinkpad);
514
515   /* (video) source */
516   overlay->srcpad =
517       gst_pad_new_from_template (gst_static_pad_template_get
518       (&textoverlay_src_template_factory), "src");
519   gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
520
521   overlay->layout =
522       pango_layout_new (GST_TEXTOVERLAY_GET_CLASS (overlay)->pango_context);
523   memset (&overlay->bitmap, 0, sizeof (overlay->bitmap));
524
525   overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
526   overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
527   overlay->x0 = overlay->y0 = 0;
528
529   overlay->default_text = g_strdup ("");
530   overlay->need_render = TRUE;
531
532   gst_element_set_loop_function (GST_ELEMENT (overlay), gst_textoverlay_loop);
533 }
534
535
536 static void
537 gst_textoverlay_set_property (GObject * object, guint prop_id,
538     const GValue * value, GParamSpec * pspec)
539 {
540   GstTextOverlay *overlay;
541
542   /* it's not null if we got it, but it might not be ours */
543   g_return_if_fail (GST_IS_TEXTOVERLAY (object));
544   overlay = GST_TEXTOVERLAY (object);
545
546   switch (prop_id) {
547
548     case ARG_TEXT:
549       if (overlay->default_text) {
550         g_free (overlay->default_text);
551       }
552       overlay->default_text = g_strdup (g_value_get_string (value));
553       overlay->need_render = TRUE;
554       break;
555
556     case ARG_VALIGN:
557       if (strcasecmp (g_value_get_string (value), "baseline") == 0)
558         overlay->valign = GST_TEXT_OVERLAY_VALIGN_BASELINE;
559       else if (strcasecmp (g_value_get_string (value), "bottom") == 0)
560         overlay->valign = GST_TEXT_OVERLAY_VALIGN_BOTTOM;
561       else if (strcasecmp (g_value_get_string (value), "top") == 0)
562         overlay->valign = GST_TEXT_OVERLAY_VALIGN_TOP;
563       else
564         g_warning ("Invalid 'valign' property value: %s",
565             g_value_get_string (value));
566       break;
567
568     case ARG_HALIGN:
569       if (strcasecmp (g_value_get_string (value), "left") == 0)
570         overlay->halign = GST_TEXT_OVERLAY_HALIGN_LEFT;
571       else if (strcasecmp (g_value_get_string (value), "right") == 0)
572         overlay->halign = GST_TEXT_OVERLAY_HALIGN_RIGHT;
573       else if (strcasecmp (g_value_get_string (value), "center") == 0)
574         overlay->halign = GST_TEXT_OVERLAY_HALIGN_CENTER;
575       else
576         g_warning ("Invalid 'halign' property value: %s",
577             g_value_get_string (value));
578       break;
579
580     case ARG_X0:
581       overlay->x0 = g_value_get_int (value);
582       break;
583
584     case ARG_Y0:
585       overlay->y0 = g_value_get_int (value);
586       break;
587
588     case ARG_FONT_DESC:
589     {
590       PangoFontDescription *desc;
591
592       desc = pango_font_description_from_string (g_value_get_string (value));
593       if (desc) {
594         g_message ("font description set: %s", g_value_get_string (value));
595         pango_layout_set_font_description (overlay->layout, desc);
596         pango_font_description_free (desc);
597         render_text (overlay);
598       } else
599         g_warning ("font description parse failed: %s",
600             g_value_get_string (value));
601       break;
602     }
603
604     default:
605       break;
606   }
607 }
608
609 static void
610 gst_textoverlay_get_property (GObject * object, guint prop_id, GValue * value,
611     GParamSpec * pspec)
612 {
613   GstTextOverlay *overlay;
614
615   /* it's not null if we got it, but it might not be ours */
616   g_return_if_fail (GST_IS_TEXTOVERLAY (object));
617   overlay = GST_TEXTOVERLAY (object);
618
619   switch (prop_id) {
620     default:
621       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
622       break;
623   }
624 }
625
626 static gboolean
627 plugin_init (GstPlugin * plugin)
628 {
629   if (!gst_element_register (plugin, "textoverlay", GST_RANK_PRIMARY,
630           GST_TYPE_TEXTOVERLAY))
631     return FALSE;
632
633   /*texttestsrc_plugin_init(module, plugin); */
634   /*subparse_plugin_init(module, plugin); */
635   return TRUE;
636 }
637
638 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
639     GST_VERSION_MINOR,
640     "textoverlay",
641     "Text overlay", plugin_init, VERSION, "GPL", GST_PACKAGE, GST_ORIGIN)