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