close #333784 unref the result of gst_pad_get_parent() by: Christophe Fergeau.
[platform/upstream/gstreamer.git] / gst / flx / gstflxdec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <string.h>
24
25 #include "flx_fmt.h"
26 #include "gstflxdec.h"
27 #include <gst/video/video.h>
28
29 #define JIFFIE  (GST_SECOND/70)
30
31 GST_DEBUG_CATEGORY_STATIC (flxdec_debug);
32 #define GST_CAT_DEFAULT flxdec_debug
33
34 /* flx element information */
35 static GstElementDetails flxdec_details = {
36   "FLX Decoder",
37   "Codec/Decoder/Audio",
38   "FLX decoder",
39   "Sepp Wijnands <mrrazz@garbage-coderz.net>, Zeeshan Ali <zeenix@gmail.com>"
40 };
41
42 /* Flx signals and args */
43 enum
44 {
45   /* FILL ME */
46   LAST_SIGNAL
47 };
48
49 enum
50 {
51   ARG_0
52 };
53
54 /* input */
55 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS ("video/x-fli")
59     );
60
61 /* output */
62 static GstStaticPadTemplate src_video_factory = GST_STATIC_PAD_TEMPLATE ("src",
63     GST_PAD_SRC,
64     GST_PAD_ALWAYS,
65     GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
66     );
67
68
69 static void gst_flxdec_class_init (GstFlxDecClass * klass);
70 static void gst_flxdec_base_init (GstFlxDecClass * klass);
71 static void gst_flxdec_init (GstFlxDec * flxdec);
72
73 static GstFlowReturn gst_flxdec_chain (GstPad * pad, GstBuffer * buf);
74
75 static GstStateChangeReturn gst_flxdec_change_state (GstElement * element,
76     GstStateChange transition);
77
78 static gboolean gst_flxdec_src_query_handler (GstPad * pad, GstQuery * query);
79 static gboolean gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event);
80 static gboolean gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event);
81
82 static void gst_flxdec_set_property (GObject * object, guint prop_id,
83     const GValue * value, GParamSpec * pspec);
84 static void gst_flxdec_get_property (GObject * object, guint prop_id,
85     GValue * value, GParamSpec * pspec);
86
87
88 static void flx_decode_color (GstFlxDec *, guchar *, guchar *, gint);
89 static void flx_decode_brun (GstFlxDec *, guchar *, guchar *);
90 static void flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *);
91 static void flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *);
92
93 #define rndalign(off) ((off) + ((off) & 1))
94
95 static GstElementClass *parent_class = NULL;
96
97 GType
98 gst_flxdec_get_type (void)
99 {
100   static GType flxdec_type = 0;
101
102   if (!flxdec_type) {
103     static const GTypeInfo flxdec_info = {
104       sizeof (GstFlxDecClass),
105       (GBaseInitFunc) gst_flxdec_base_init,
106       NULL,
107       (GClassInitFunc) gst_flxdec_class_init,
108       NULL,
109       NULL,
110       sizeof (GstFlxDec),
111       0,
112       (GInstanceInitFunc) gst_flxdec_init,
113     };
114
115     flxdec_type =
116         g_type_register_static (GST_TYPE_ELEMENT, "GstFlxDec", &flxdec_info, 0);
117   }
118   return flxdec_type;
119 }
120
121 static void
122 gst_flxdec_base_init (GstFlxDecClass * klass)
123 {
124   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
125
126   gst_element_class_set_details (gstelement_class, &flxdec_details);
127   gst_element_class_add_pad_template (gstelement_class,
128       gst_static_pad_template_get (&sink_factory));
129   gst_element_class_add_pad_template (gstelement_class,
130       gst_static_pad_template_get (&src_video_factory));
131 }
132
133 static void
134 gst_flxdec_class_init (GstFlxDecClass * klass)
135 {
136   GObjectClass *gobject_class;
137   GstElementClass *gstelement_class;
138
139   gobject_class = (GObjectClass *) klass;
140   gstelement_class = (GstElementClass *) klass;
141
142   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
143
144   GST_DEBUG_CATEGORY_INIT (flxdec_debug, "flxdec", 0, "FLX video decoder");
145
146   gobject_class->set_property = gst_flxdec_set_property;
147   gobject_class->get_property = gst_flxdec_get_property;
148
149   gstelement_class->change_state = gst_flxdec_change_state;
150 }
151
152 static void
153 gst_flxdec_init (GstFlxDec * flxdec)
154 {
155   flxdec->sinkpad =
156       gst_pad_new_from_template (gst_static_pad_template_get (&sink_factory),
157       "sink");
158   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->sinkpad);
159   gst_pad_set_chain_function (flxdec->sinkpad, gst_flxdec_chain);
160   gst_pad_set_event_function (flxdec->sinkpad, gst_flxdec_sink_event_handler);
161
162   flxdec->srcpad =
163       gst_pad_new_from_template (gst_static_pad_template_get
164       (&src_video_factory), "src");
165   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->srcpad);
166   gst_pad_set_query_function (flxdec->srcpad, gst_flxdec_src_query_handler);
167   gst_pad_set_event_function (flxdec->srcpad, gst_flxdec_src_event_handler);
168
169   gst_pad_use_fixed_caps (flxdec->srcpad);
170
171   flxdec->frame = NULL;
172   flxdec->delta = NULL;
173
174   flxdec->adapter = gst_adapter_new ();
175 }
176
177 static gboolean
178 gst_flxdec_src_query_handler (GstPad * pad, GstQuery * query)
179 {
180   GstFlxDec *flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
181   gboolean ret = FALSE;
182
183   switch (GST_QUERY_TYPE (query)) {
184     case GST_QUERY_DURATION:
185     {
186       GstFormat format;
187
188       gst_query_parse_duration (query, &format, NULL);
189
190       if (format != GST_FORMAT_TIME)
191         goto done;
192
193       gst_query_set_duration (query, format, flxdec->duration);
194
195       ret = TRUE;
196     }
197     default:
198       break;
199   }
200 done:
201   gst_object_unref (flxdec);
202
203   return ret;
204 }
205
206 static gboolean
207 gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event)
208 {
209   GstFlxDec *flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
210   gboolean ret;
211
212   /* TODO: implement the seek and other event handling */
213
214   ret = gst_pad_push_event (flxdec->sinkpad, event);
215
216   gst_object_unref (flxdec);
217
218   return ret;
219 }
220
221 static gboolean
222 gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event)
223 {
224   GstFlxDec *flxdec;
225   gboolean ret;
226
227   flxdec = GST_FLXDEC (gst_pad_get_parent (pad));
228
229   ret = gst_pad_push_event (flxdec->srcpad, event);
230
231   gst_object_unref (flxdec);
232   return ret;
233 }
234
235 static void
236 flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data,
237     guchar * dest)
238 {
239   FlxFrameChunk *hdr;
240
241   g_return_if_fail (data != NULL);
242
243   while (count--) {
244     hdr = (FlxFrameChunk *) data;
245     FLX_FRAME_CHUNK_FIX_ENDIANNESS (hdr);
246     data += FlxFrameChunkSize;
247
248     switch (hdr->id) {
249       case FLX_COLOR64:
250         flx_decode_color (flxdec, data, dest, 2);
251         data += rndalign (hdr->size) - FlxFrameChunkSize;
252         break;
253
254       case FLX_COLOR256:
255         flx_decode_color (flxdec, data, dest, 0);
256         data += rndalign (hdr->size) - FlxFrameChunkSize;
257         break;
258
259       case FLX_BRUN:
260         flx_decode_brun (flxdec, data, dest);
261         data += rndalign (hdr->size) - FlxFrameChunkSize;
262         break;
263
264       case FLX_LC:
265         flx_decode_delta_fli (flxdec, data, dest);
266         data += rndalign (hdr->size) - FlxFrameChunkSize;
267         break;
268
269       case FLX_SS2:
270         flx_decode_delta_flc (flxdec, data, dest);
271         data += rndalign (hdr->size) - FlxFrameChunkSize;
272         break;
273
274       case FLX_BLACK:
275         memset (dest, 0, flxdec->size);
276         break;
277
278       case FLX_MINI:
279         data += rndalign (hdr->size) - FlxFrameChunkSize;
280         break;
281
282       default:
283         GST_WARNING ("Unimplented chunk type: 0x%02x size: %d - skipping",
284             hdr->id, hdr->size);
285         data += rndalign (hdr->size) - FlxFrameChunkSize;
286         break;
287     }
288   }
289 }
290
291
292 static void
293 flx_decode_color (GstFlxDec * flxdec, guchar * data, guchar * dest, gint scale)
294 {
295   guint packs, count, indx;
296
297   g_return_if_fail (flxdec != NULL);
298
299   packs = (data[0] + (data[1] << 8));
300
301   data += 2;
302   indx = 0;
303
304   GST_LOG ("GstFlxDec: cmap packs: %d", packs);
305   while (packs--) {
306     /* color map index + skip count */
307     indx += *data++;
308
309     /* number of rgb triplets */
310     count = *data++ & 0xff;
311     if (count == 0)
312       count = 256;
313
314     GST_LOG ("GstFlxDec: cmap count: %d (indx: %d)", count, indx);
315     flx_set_palette_vector (flxdec->converter, indx, count, data, scale);
316
317     data += (count * 3);
318   }
319 }
320
321 static void
322 flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest)
323 {
324   gulong count, lines, row;
325   guchar x;
326
327   g_return_if_fail (flxdec != NULL);
328
329   lines = flxdec->hdr.height;
330   while (lines--) {
331     /* packet count.  
332      * should not be used anymore, since the flc format can
333      * contain more then 255 RLE packets. we use the frame 
334      * width instead. 
335      */
336     data++;
337
338     row = flxdec->hdr.width;
339     while (row) {
340       count = *data++;
341
342       if (count > 0x7f) {
343         /* literal run */
344         count = 0x100 - count;
345         row -= count;
346
347         while (count--)
348           *dest++ = *data++;
349
350       } else {
351         /* replicate run */
352         row -= count;
353         x = *data++;
354
355         while (count--)
356           *dest++ = x;
357       }
358     }
359   }
360 }
361
362 static void
363 flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
364 {
365   gulong count, packets, lines, start_line, start_l;
366   guchar *start_p, x;
367
368   g_return_if_fail (flxdec != NULL);
369   g_return_if_fail (flxdec->delta != NULL);
370
371   /* use last frame for delta */
372   memcpy (dest, GST_BUFFER_DATA (flxdec->delta),
373       GST_BUFFER_SIZE (flxdec->delta));
374
375   start_line = (data[0] + (data[1] << 8));
376   lines = (data[2] + (data[3] << 8));
377   data += 4;
378
379   /* start position of delta */
380   dest += (flxdec->hdr.width * start_line);
381   start_p = dest;
382   start_l = lines;
383
384   while (lines--) {
385     /* packet count */
386     packets = *data++;
387
388     while (packets--) {
389       /* skip count */
390       dest += *data++;
391
392       /* RLE count */
393       count = *data++;
394
395       if (count > 0x7f) {
396         /* literal run */
397         count = 0x100 - count;
398         x = *data++;
399
400         while (count--)
401           *dest++ = x;
402
403       } else {
404         /* replicate run */
405         while (count--)
406           *dest++ = *data++;
407       }
408     }
409     start_p += flxdec->hdr.width;
410     dest = start_p;
411   }
412 }
413
414 static void
415 flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest)
416 {
417   gulong count, lines, start_l, opcode;
418   guchar *start_p;
419
420   g_return_if_fail (flxdec != NULL);
421   g_return_if_fail (flxdec->delta != NULL);
422
423   /* use last frame for delta */
424   memcpy (dest, GST_BUFFER_DATA (flxdec->delta),
425       GST_BUFFER_SIZE (flxdec->delta));
426
427   lines = (data[0] + (data[1] << 8));
428   data += 2;
429
430   start_p = dest;
431   start_l = lines;
432
433   while (lines) {
434     dest = start_p + (flxdec->hdr.width * (start_l - lines));
435
436     /* process opcode(s) */
437     while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) {
438       data += 2;
439       if ((opcode & 0xc000) == 0xc000) {
440         /* skip count */
441         start_l += (0x10000 - opcode);
442         dest += flxdec->hdr.width * (0x10000 - opcode);
443       } else {
444         /* last pixel */
445         dest += flxdec->hdr.width;
446         *dest++ = (opcode & 0xff);
447       }
448     }
449     data += 2;
450
451     /* last opcode is the packet count */
452     while (opcode--) {
453       /* skip count */
454       dest += *data++;
455
456       /* RLE count */
457       count = *data++;
458
459       if (count > 0x7f) {
460         /* replicate word run */
461         count = 0x100 - count;
462         while (count--) {
463           *dest++ = data[0];
464           *dest++ = data[1];
465         }
466         data += 2;
467       } else {
468         /* literal word run */
469         while (count--) {
470           *dest++ = *data++;
471           *dest++ = *data++;
472         }
473       }
474     }
475     lines--;
476   }
477 }
478
479 static GstFlowReturn
480 gst_flxdec_chain (GstPad * pad, GstBuffer * buf)
481 {
482   GstCaps *caps;
483   guint avail;
484   GstFlowReturn res = GST_FLOW_OK;
485
486   GstFlxDec *flxdec;
487   FlxHeader *flxh;
488
489   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
490   flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
491   g_return_val_if_fail (flxdec != NULL, GST_FLOW_ERROR);
492
493   gst_adapter_push (flxdec->adapter, buf);
494   avail = gst_adapter_available (flxdec->adapter);
495
496   if (flxdec->state == GST_FLXDEC_READ_HEADER) {
497     if (avail >= FlxHeaderSize) {
498       const guint8 *data = gst_adapter_peek (flxdec->adapter, FlxHeaderSize);
499
500       memcpy ((gchar *) & flxdec->hdr, data, FlxHeaderSize);
501       FLX_HDR_FIX_ENDIANNESS (&(flxdec->hdr));
502       gst_adapter_flush (flxdec->adapter, FlxHeaderSize);
503
504       flxh = &flxdec->hdr;
505
506       /* check header */
507       if (flxh->type != FLX_MAGICHDR_FLI &&
508           flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX)
509         goto wrong_type;
510
511       GST_LOG ("size      :  %d", flxh->size);
512       GST_LOG ("frames    :  %d", flxh->frames);
513       GST_LOG ("width     :  %d", flxh->width);
514       GST_LOG ("height    :  %d", flxh->height);
515       GST_LOG ("depth     :  %d", flxh->depth);
516       GST_LOG ("speed     :  %d", flxh->speed);
517
518       flxdec->next_time = 0;
519
520       if (flxh->type == FLX_MAGICHDR_FLI) {
521         flxdec->frame_time = JIFFIE * flxh->speed;
522       } else if (flxh->speed == 0) {
523         flxdec->frame_time = GST_SECOND / 70;
524       } else {
525         flxdec->frame_time = flxh->speed * GST_MSECOND;
526       }
527
528       flxdec->duration = flxh->frames * flxdec->frame_time;
529       GST_LOG ("duration   :  %" GST_TIME_FORMAT,
530           GST_TIME_ARGS (flxdec->duration));
531
532       caps = gst_caps_from_string (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN);
533       gst_caps_set_simple (caps,
534           "width", G_TYPE_INT, flxh->width,
535           "height", G_TYPE_INT, flxh->height,
536           "framerate", GST_TYPE_FRACTION, (gint) GST_MSECOND,
537           (gint) flxdec->frame_time / 1000, NULL);
538
539       gst_pad_set_caps (flxdec->srcpad, caps);
540       gst_caps_unref (caps);
541
542       if (flxh->depth <= 8)
543         flxdec->converter =
544             flx_colorspace_converter_new (flxh->width, flxh->height);
545
546       if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) {
547         GST_LOG ("(FLC) aspect_dx :  %d", flxh->aspect_dx);
548         GST_LOG ("(FLC) aspect_dy :  %d", flxh->aspect_dy);
549         GST_LOG ("(FLC) oframe1   :  0x%08x", flxh->oframe1);
550         GST_LOG ("(FLC) oframe2   :  0x%08x", flxh->oframe2);
551       }
552
553       flxdec->size = (flxh->width * flxh->height);
554
555       /* create delta and output frame */
556       flxdec->frame = gst_buffer_new ();
557       flxdec->delta = gst_buffer_new ();
558       GST_BUFFER_DATA (flxdec->frame) = g_malloc (flxdec->size);
559       GST_BUFFER_MALLOCDATA (flxdec->frame) = GST_BUFFER_DATA (flxdec->frame);
560       GST_BUFFER_SIZE (flxdec->frame) = flxdec->size;
561       GST_BUFFER_DATA (flxdec->delta) = g_malloc (flxdec->size);
562       GST_BUFFER_MALLOCDATA (flxdec->delta) = GST_BUFFER_DATA (flxdec->delta);
563       GST_BUFFER_SIZE (flxdec->delta) = flxdec->size;
564
565       flxdec->state = GST_FLXDEC_PLAYING;
566     }
567   } else if (flxdec->state == GST_FLXDEC_PLAYING) {
568     GstBuffer *out;
569
570     /* while we have enough data in the adapter */
571     while (avail >= FlxFrameChunkSize) {
572       FlxFrameChunk flxfh;
573       guchar *chunk;
574       const guint8 *data;
575
576       chunk = NULL;
577       data = gst_adapter_peek (flxdec->adapter, FlxFrameChunkSize);
578       memcpy (&flxfh, data, FlxFrameChunkSize);
579       FLX_FRAME_CHUNK_FIX_ENDIANNESS (&flxfh);
580
581       switch (flxfh.id) {
582         case FLX_FRAME_TYPE:
583           /* check if we have the complete frame */
584           if (avail < flxfh.size)
585             goto need_more_data;
586
587           /* flush header */
588           gst_adapter_flush (flxdec->adapter, FlxFrameChunkSize);
589
590           chunk = gst_adapter_take (flxdec->adapter,
591               flxfh.size - FlxFrameChunkSize);
592           FLX_FRAME_TYPE_FIX_ENDIANNESS ((FlxFrameType *) chunk);
593           if (((FlxFrameType *) chunk)->chunks == 0)
594             break;
595
596           /* create 32 bits output frame */
597           res = gst_pad_alloc_buffer_and_set_caps (flxdec->srcpad,
598               GST_BUFFER_OFFSET_NONE,
599               flxdec->size * 4, GST_PAD_CAPS (flxdec->srcpad), &out);
600           if (res != GST_FLOW_OK)
601             break;
602
603           /* decode chunks */
604           flx_decode_chunks (flxdec,
605               ((FlxFrameType *) chunk)->chunks,
606               chunk + FlxFrameTypeSize, GST_BUFFER_DATA (flxdec->frame));
607
608           /* save copy of the current frame for possible delta. */
609           memcpy (GST_BUFFER_DATA (flxdec->delta),
610               GST_BUFFER_DATA (flxdec->frame), GST_BUFFER_SIZE (flxdec->delta));
611
612           /* convert current frame. */
613           flx_colorspace_convert (flxdec->converter,
614               GST_BUFFER_DATA (flxdec->frame), GST_BUFFER_DATA (out));
615
616           GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
617           flxdec->next_time += flxdec->frame_time;
618
619           gst_pad_push (flxdec->srcpad, out);
620           break;
621       }
622
623       if (chunk)
624         g_free (chunk);
625
626       avail = gst_adapter_available (flxdec->adapter);
627     }
628   }
629 need_more_data:
630   gst_object_unref (flxdec);
631   return res;
632
633   /* ERRORS */
634 wrong_type:
635   {
636     GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL),
637         ("not a flx file (type %x)", flxh->type));
638     gst_object_unref (flxdec);
639     return GST_FLOW_ERROR;
640   }
641 }
642
643 static GstStateChangeReturn
644 gst_flxdec_change_state (GstElement * element, GstStateChange transition)
645 {
646   GstFlxDec *flxdec;
647   GstStateChangeReturn ret;
648
649   flxdec = GST_FLXDEC (element);
650
651   switch (transition) {
652     case GST_STATE_CHANGE_NULL_TO_READY:
653       break;
654     case GST_STATE_CHANGE_READY_TO_PAUSED:
655       gst_adapter_clear (flxdec->adapter);
656       flxdec->state = GST_FLXDEC_READ_HEADER;
657       break;
658     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
659       break;
660     default:
661       break;
662   }
663
664   ret = parent_class->change_state (element, transition);
665
666   switch (transition) {
667     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
668       break;
669     case GST_STATE_CHANGE_PAUSED_TO_READY:
670       if (flxdec->frame) {
671         gst_buffer_unref (flxdec->frame);
672         flxdec->frame = NULL;
673       }
674       if (flxdec->delta) {
675         gst_buffer_unref (flxdec->delta);
676         flxdec->delta = NULL;
677       }
678       break;
679     case GST_STATE_CHANGE_READY_TO_NULL:
680       break;
681     default:
682       break;
683   }
684   return ret;
685 }
686
687 static void
688 gst_flxdec_set_property (GObject * object, guint prop_id, const GValue * value,
689     GParamSpec * pspec)
690 {
691   GstFlxDec *flxdec;
692
693   g_return_if_fail (GST_IS_FLXDEC (object));
694   flxdec = GST_FLXDEC (object);
695
696   switch (prop_id) {
697     default:
698       break;
699   }
700 }
701
702 static void
703 gst_flxdec_get_property (GObject * object, guint prop_id, GValue * value,
704     GParamSpec * pspec)
705 {
706   GstFlxDec *flxdec;
707
708   g_return_if_fail (GST_IS_FLXDEC (object));
709   flxdec = GST_FLXDEC (object);
710
711   switch (prop_id) {
712     default:
713       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
714       break;
715   }
716 }
717
718 static gboolean
719 plugin_init (GstPlugin * plugin)
720 {
721   return gst_element_register (plugin, "flxdec",
722       GST_RANK_PRIMARY, GST_TYPE_FLXDEC);
723 }
724
725 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
726     GST_VERSION_MINOR,
727     "flxdec",
728     "FLX video decoder",
729     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)