GstPadTemplate <-> gst_pad_template <-> GST_PAD_TEMPLATE same with *factory and typefind.
[platform/upstream/gst-plugins-good.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 #include <string.h>
21
22 #include "flx_fmt.h"
23 #include "gstflxdec.h"
24
25 #define JIFFIE  (1000000/70)
26
27 static GstCaps* flxdec_type_find(GstBuffer *buf, gpointer private);
28
29 /* flx element information */
30 static GstElementDetails flxdec_details = {
31   "FLX Decoder",
32   "flxdec",
33   "FLX decoder",
34   VERSION,
35   "Sepp Wijnands <mrrazz@garbage-coderz.net>"
36   "(C) 2001",
37 };
38
39 static GstTypeDefinition flxdec_definition = {
40   "flxdec_video/fli",
41   "video/fli",
42   ".flc .fli",
43   flxdec_type_find,
44 };
45
46 /* Flx signals and args */
47 enum {
48   /* FILL ME */
49   LAST_SIGNAL
50 };
51
52 enum {
53   ARG_0
54 };
55
56 /* input */
57 GST_PAD_TEMPLATE_FACTORY (sink_factory,
58   "sink",          
59   GST_PAD_SINK, 
60   GST_PAD_ALWAYS,
61   GST_CAPS_NEW (
62     "flxdec_sink",         
63     "video/fli",
64      NULL
65   )
66 )
67
68 /* output */
69 GST_PAD_TEMPLATE_FACTORY (src_video_factory,
70   "src",
71   GST_PAD_SRC,
72   GST_PAD_ALWAYS,
73   GST_CAPS_NEW (
74     "src_video",
75     "video/raw",
76       "format",       GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R', 'G', 'B', ' ')),
77         "bpp",        GST_PROPS_INT (32),
78         "depth",      GST_PROPS_INT (32),
79         "endianness", GST_PROPS_INT (G_LITTLE_ENDIAN),
80         "red_mask",   GST_PROPS_INT (0x00ff0000),
81         "green_mask", GST_PROPS_INT (0x0000ff00),
82         "blue_mask",  GST_PROPS_INT (0x000000ff),
83         "width",      GST_PROPS_INT_RANGE(320, 1280), 
84         "height",     GST_PROPS_INT_RANGE(200, 1024)
85   )
86 )
87
88
89 static void     gst_flxdec_class_init   (GstFlxDecClass *klass);
90 static void     gst_flxdec_init         (GstFlxDec *flxdec);
91
92 static void     gst_flxdec_loop         (GstElement *element);
93
94 static GstElementStateReturn 
95                 gst_flxdec_change_state (GstElement *element);
96
97 static void     gst_flxdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
98 static void     gst_flxdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
99
100
101 static void     flx_decode_color        (GstFlxDec *, guchar *, guchar *, gint);
102 static void     flx_decode_brun         (GstFlxDec *, guchar *, guchar *);
103 static void     flx_decode_delta_fli    (GstFlxDec *, guchar *, guchar *);
104 static void     flx_decode_delta_flc    (GstFlxDec *, guchar *, guchar *);
105
106 #define rndalign(off) ((off) + ((off) % 2))
107
108 static GstElementClass *parent_class = NULL;
109
110 static GstCaps* 
111 flxdec_type_find (GstBuffer *buf, gpointer private)
112 {
113   guchar *data = GST_BUFFER_DATA(buf);
114   GstCaps *new;
115
116   /* check magic */
117   if ((data[4] == 0x11 || data[4] == 0x12
118        || data[4] == 0x30 || data[4] == 0x44) && data[5] == 0xaf) {
119       /* check the frame type of the first frame */
120       if ((data[132] == 0x00 || data[132] == 0xfa) && data[133] == 0xf1) {
121         g_print("GstFlxDec: found supported flx format\n");
122         new = gst_caps_new("flxdec_type_find","video/fli", NULL);
123         return new;
124       }
125   }
126   
127   return NULL; 
128 }
129
130
131 GType
132 gst_flxdec_get_type(void) 
133 {
134   static GType flxdec_type = 0;
135
136   if (!flxdec_type) {
137     static const GTypeInfo flxdec_info = {
138       sizeof(GstFlxDecClass),      NULL,
139       NULL,
140       (GClassInitFunc)gst_flxdec_class_init,
141       NULL,
142       NULL,
143       sizeof(GstFlxDec),
144       0,
145       (GInstanceInitFunc)gst_flxdec_init,
146     };
147     flxdec_type = g_type_register_static(GST_TYPE_ELEMENT, "GstFlxDec", &flxdec_info, 0);
148   }
149   return flxdec_type;
150 }
151
152 static void 
153 gst_flxdec_class_init (GstFlxDecClass *klass) 
154 {
155   GObjectClass *gobject_class;
156   GstElementClass *gstelement_class;
157
158   gobject_class = (GObjectClass*)klass;
159   gstelement_class = (GstElementClass*)klass;
160
161   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
162
163   gobject_class->set_property = gst_flxdec_set_property;  
164   gobject_class->get_property = gst_flxdec_get_property;
165
166   gstelement_class->change_state = gst_flxdec_change_state;
167 }
168
169
170   
171 static void 
172 gst_flxdec_init(GstFlxDec *flxdec) 
173 {
174   flxdec->sinkpad = gst_pad_new_from_template (
175                   GST_PAD_TEMPLATE_GET (sink_factory), "sink");
176   gst_element_add_pad(GST_ELEMENT(flxdec),flxdec->sinkpad);
177   gst_element_set_loop_function(GST_ELEMENT(flxdec),gst_flxdec_loop);
178
179   flxdec->srcpad = gst_pad_new_from_template (
180                   GST_PAD_TEMPLATE_GET (src_video_factory), "src");
181   gst_element_add_pad(GST_ELEMENT(flxdec),flxdec->srcpad);
182
183   flxdec->bs = NULL;
184   flxdec->frame = NULL;
185   flxdec->delta = NULL;
186 }
187
188 static void
189 flx_decode_chunks (GstFlxDec *flxdec , gulong count, gchar *data, gchar *dest)
190 {
191   FlxFrameChunk  *hdr;
192
193   g_return_if_fail(data != NULL);
194
195   while (count--) {
196     hdr  = (FlxFrameChunk *) data;
197     data += FlxFrameChunkSize;
198
199     switch(hdr->id) 
200     {
201       case FLX_COLOR64:
202         flx_decode_color(flxdec, data, dest, 2);
203         data += rndalign(hdr->size) - FlxFrameChunkSize;
204         break;
205
206       case FLX_COLOR256:
207         flx_decode_color(flxdec, data, dest, 0);
208         data += rndalign(hdr->size) - FlxFrameChunkSize;
209         break;
210
211       case FLX_BRUN:
212         flx_decode_brun(flxdec, data, dest);
213         data += rndalign(hdr->size) - FlxFrameChunkSize;
214         break;
215
216       case FLX_LC:
217         flx_decode_delta_fli(flxdec, data, dest);
218         data += rndalign(hdr->size) - FlxFrameChunkSize;
219         break;
220
221       case FLX_SS2:
222         flx_decode_delta_flc(flxdec, data, dest);
223         data += rndalign(hdr->size) - FlxFrameChunkSize;
224         break;
225
226       case FLX_BLACK:
227         memset(dest, 0, flxdec->size);
228         break;
229
230       case FLX_MINI:
231         data += rndalign(hdr->size) - FlxFrameChunkSize;
232         break;
233
234       default:
235         g_print("GstFlxDec: Unimplented chunk type: 0x%02x size: %d\n",
236                  hdr->id, hdr->size);
237         g_print("GstFlxDec: Skipping...\n");
238         data += rndalign(hdr->size) - FlxFrameChunkSize;
239         break;
240     }
241   }
242 }
243
244
245 static void
246 flx_decode_color(GstFlxDec *flxdec, guchar *data, guchar *dest, gint scale)
247 {
248   guint packs, count, indx;
249
250   g_return_if_fail(flxdec != NULL);
251
252   packs = (data[0] + (data[1] << 8));
253
254   data += 2;
255   indx  = 0;
256
257   g_print("GstFlxDec: cmap packs: %d\n", packs);
258   while (packs--) {
259     /* color map index + skip count */
260     indx += *data++;
261
262     /* number of rgb triplets */
263     count = *data++ & 0xff;
264     if (count == 0)
265       count = 256;
266
267     g_print("GstFlxDec: cmap count: %d (indx: %d)\n", count, indx);
268     flx_set_palette_vector(flxdec->converter, indx, count, data, scale);
269
270     data += (count * 3);
271   }
272 }
273
274 static void 
275 flx_decode_brun(GstFlxDec *flxdec, guchar *data, guchar *dest)
276 {
277   gulong  count, lines, row;
278   guchar  x;
279   
280   g_return_if_fail(flxdec != NULL);
281
282   lines = flxdec->hdr.height;
283   while(lines--) {
284     /* packet count.  
285      * should not be used anymore, since the flc format can
286      * contain more then 255 RLE packets. we use the frame 
287      * width instead. 
288      */
289     data++;
290
291     row = flxdec->hdr.width;
292     while(row) {
293       count = *data++;
294
295       if (count > 0x7f) { 
296         /* literal run */
297         count = 0x100 - count;
298         row -= count;
299
300         while(count--) 
301           *dest++ = *data++;
302         
303       } else {
304         /* replicate run */
305         row -= count;
306         x = *data++;
307
308         while(count--) 
309           *dest++ = x;
310       }
311     }
312   }
313 }
314
315 static void 
316 flx_decode_delta_fli(GstFlxDec *flxdec, guchar *data, guchar *dest)
317 {
318   gulong  count, packets, lines, start_line, start_l;
319   guchar  *start_p,  x;
320   
321   g_return_if_fail(flxdec != NULL);
322   g_return_if_fail(flxdec->delta != NULL);
323
324
325   /* use last frame for delta */
326   memcpy(dest, GST_BUFFER_DATA(flxdec->delta), 
327          GST_BUFFER_SIZE(flxdec->delta));
328
329   start_line = (data[0] + (data[1] << 8));
330   lines      = (data[2] + (data[3] << 8));
331   data    += 4;
332
333   /* start position of delta */
334   dest += (flxdec->hdr.width * start_line);
335   start_p = dest;
336   start_l = lines;
337
338   while(lines--) {
339     /* packet count */
340     packets = *data++;
341
342     while(packets--) {
343       /* skip count */
344       dest += *data++;
345
346       /* RLE count */
347       count = *data++;
348
349       if (count > 0x7f) { 
350         /* literal run */
351         count = 0x100 - count;
352         x = *data++;
353
354         while (count--)
355           *dest++ = x;
356
357       } else {
358         /* replicate run */
359         while (count--) 
360           *dest++ = *data++;
361       }
362     }
363     start_p += flxdec->hdr.width;
364     dest = start_p;
365   }                  
366 }
367
368 static void 
369 flx_decode_delta_flc(GstFlxDec *flxdec, guchar *data, guchar *dest)
370 {
371   gulong  count, lines, start_l, opcode;
372   guchar  *start_p;
373   
374   g_return_if_fail(flxdec != NULL);
375   g_return_if_fail(flxdec->delta != NULL);
376
377
378   /* use last frame for delta */
379   memcpy(dest, GST_BUFFER_DATA(flxdec->delta), 
380          GST_BUFFER_SIZE(flxdec->delta));
381
382   lines      = (data[0] + (data[1] << 8));
383   data    += 2;
384
385   start_p    = dest;
386   start_l    = lines;
387
388   while (lines) {
389     dest = start_p + (flxdec->hdr.width * (start_l - lines));
390
391     /* process opcode(s) */
392     while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) {
393       data += 2;
394       if ((opcode & 0xc000) == 0xc000) {
395         /* skip count */
396         start_l += (0x10000 - opcode);  
397         dest    += flxdec->hdr.width * (0x10000 - opcode);
398       } else {
399         /* last pixel */
400         dest    += flxdec->hdr.width;
401         *dest++  = (opcode & 0xff);
402       }        
403     }
404     data += 2;
405
406     /* last opcode is the packet count */
407     while(opcode--) {
408       /* skip count */
409       dest += *data++;
410   
411       /* RLE count */
412       count = *data++;
413       
414       if (count > 0x7f) {
415         /* replicate word run */
416         count = 0x100 - count;
417         while (count--) {
418           *dest++ = data[0];
419           *dest++ = data[1];
420         }
421         data += 2;
422       } else {
423         /* literal word run */
424         while (count--) {
425           *dest++ = *data++;
426           *dest++ = *data++;
427         }
428       }
429     }
430     lines--;
431   }
432 }
433           
434 static GstBuffer*
435 flx_get_data(GstFlxDec *flxdec, gulong size)
436 {
437   GstBuffer *retbuf;
438
439   g_return_val_if_fail (flxdec != NULL, NULL);
440
441   retbuf = gst_bytestream_read (flxdec->bs, size);
442   if (!retbuf) {
443     GstEvent *event;
444     guint32 remaining;
445
446     gst_bytestream_get_status (flxdec->bs, &remaining, &event);
447     gst_pad_event_default (flxdec->sinkpad, event);
448   }
449
450   return retbuf;
451 }
452
453
454 static void 
455 gst_flxdec_loop (GstElement *element) 
456 {
457   GstBuffer  *buf;  
458   GstBuffer  *databuf;
459   guchar     *data, *chunk;
460
461   GstFlxDec         *flxdec;  
462   FlxHeader      *flxh;
463   FlxFrameChunk  *flxfh;
464   
465   g_return_if_fail (element != NULL);
466   g_return_if_fail (GST_IS_FLXDEC(element));
467
468   GST_DEBUG (0, "entering loop function");
469   
470   flxdec = GST_FLXDEC(element);
471
472   if (flxdec->state == GST_FLXDEC_READ_HEADER) {
473     databuf = flx_get_data(flxdec, FlxHeaderSize);
474
475     if (!databuf) {
476       g_print ("empty buffer\n");
477       return;
478     }
479
480     data = GST_BUFFER_DATA(databuf);
481
482     memcpy((char *) &flxdec->hdr, data, sizeof(FlxHeader));
483
484     gst_buffer_unref (databuf);
485
486     flxh = &flxdec->hdr;
487
488     /* check header */
489     if (flxh->type != FLX_MAGICHDR_FLI &&
490       flxh->type != FLX_MAGICHDR_FLC &&
491       flxh->type != FLX_MAGICHDR_FLX) {
492       gst_element_error (element, "not a flx file (type %d)\n", flxh->type);
493       return;
494     }
495   
496   
497     g_print("GstFlxDec:       size      :  %d\n", flxh->size);
498     g_print("GstFlxDec:       frames    :  %d\n", flxh->frames);
499     g_print("GstFlxDec:       width     :  %d\n", flxh->width);
500     g_print("GstFlxDec:       height    :  %d\n", flxh->height);
501     g_print("GstFlxDec:       depth     :  %d\n", flxh->depth);
502     g_print("GstFlxDec:       speed     :  %d\n", flxh->speed);
503
504     flxdec->next_time = 0;
505
506     if (flxh->type == FLX_MAGICHDR_FLI) {
507       flxdec->frame_time = JIFFIE * flxh->speed;
508     }
509     else {
510       flxdec->frame_time = flxh->speed * 1000;
511     }
512     
513     gst_pad_try_set_caps (flxdec->srcpad,
514                 gst_caps_new (
515                   "src_video",
516                   "video/raw",
517                   gst_props_new (
518                     "format",       GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R', 'G', 'B', ' ')),
519                       "bpp",        GST_PROPS_INT (32),
520                       "depth",      GST_PROPS_INT (32),
521                       "endianness", GST_PROPS_INT (G_LITTLE_ENDIAN),
522                       "red_mask",   GST_PROPS_INT (0x00ff0000),
523                       "green_mask", GST_PROPS_INT (0x0000ff00),
524                       "blue_mask",  GST_PROPS_INT (0x000000ff),
525                       "width",      GST_PROPS_INT (flxh->width), 
526                       "height",     GST_PROPS_INT (flxh->height),
527                     NULL)));
528
529     if (flxh->depth <= 8) 
530       flxdec->converter = flx_colorspace_converter_new(flxh->width, flxh->height);
531
532     if (flxh->type == FLX_MAGICHDR_FLC || 
533         flxh->type == FLX_MAGICHDR_FLX) {
534       g_print("GstFlxDec: (FLC) aspect_dx :  %d\n", flxh->aspect_dx);
535       g_print("GstFlxDec: (FLC) aspect_dy :  %d\n", flxh->aspect_dy);
536       g_print("GstFlxDec: (FLC) oframe1   :  0x%08x\n", flxh->oframe1);
537       g_print("GstFlxDec: (FLC) oframe2   :  0x%08x\n", flxh->oframe2);
538     }
539
540   
541     flxdec->size = (flxh->width * flxh->height);
542   
543     /* create delta and output frame */
544     flxdec->frame = gst_buffer_new();
545     flxdec->delta = gst_buffer_new();
546     GST_BUFFER_DATA(flxdec->frame) = g_malloc(flxdec->size);
547     GST_BUFFER_SIZE(flxdec->frame) = flxdec->size;
548     GST_BUFFER_DATA(flxdec->delta) = g_malloc(flxdec->size);
549     GST_BUFFER_SIZE(flxdec->delta) = flxdec->size;
550
551     flxdec->state = GST_FLXDEC_PLAYING;
552   }
553   else if (flxdec->state == GST_FLXDEC_PLAYING) {
554     GstBuffer *out;
555
556     databuf = flx_get_data(flxdec, FlxFrameChunkSize);
557     if (!databuf)
558       return;
559
560     flxfh = (FlxFrameChunk *) GST_BUFFER_DATA(databuf);
561     
562     switch(flxfh->id) {
563       case FLX_FRAME_TYPE:
564         buf = flx_get_data(flxdec, flxfh->size-FlxFrameChunkSize);
565  
566         chunk = GST_BUFFER_DATA(buf);
567  
568         if (((FlxFrameType *)chunk)->chunks == 0)
569           break;
570
571         /* create 32 bits output frame */
572         out = gst_buffer_new();
573         GST_BUFFER_DATA(out) = g_malloc(flxdec->size * 4);
574         GST_BUFFER_SIZE(out) = flxdec->size * 4;
575
576         /* decode chunks */
577         flx_decode_chunks(flxdec, 
578                          ((FlxFrameType *)chunk)->chunks, 
579                          GST_BUFFER_DATA(buf) + FlxFrameTypeSize,
580                          GST_BUFFER_DATA(flxdec->frame));
581  
582         /* destroy input buffer*/
583         gst_buffer_unref(buf);
584     
585         /* save copy of the current frame for possible delta. */
586         memcpy(GST_BUFFER_DATA(flxdec->delta), 
587                GST_BUFFER_DATA(flxdec->frame), 
588                GST_BUFFER_SIZE(flxdec->delta));
589
590         /* convert current frame. */
591         flx_colorspace_convert(flxdec->converter,
592              GST_BUFFER_DATA(flxdec->frame),
593              GST_BUFFER_DATA(out));
594
595         GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
596         flxdec->next_time += flxdec->frame_time;
597
598         gst_pad_push(flxdec->srcpad, out);
599         
600         break;
601     }
602
603     /* destroy header buffer */
604     gst_buffer_unref(databuf);
605   }
606 }
607
608 static GstElementStateReturn 
609 gst_flxdec_change_state (GstElement *element)
610 {
611   GstFlxDec *flxdec;
612
613   flxdec = GST_FLXDEC(element);
614
615   switch (GST_STATE_TRANSITION (element)) {
616     case GST_STATE_NULL_TO_READY:
617       break;
618     case GST_STATE_READY_TO_PAUSED:
619       flxdec->bs = gst_bytestream_new (flxdec->sinkpad);
620       flxdec->state = GST_FLXDEC_READ_HEADER;
621       break;
622     case GST_STATE_PAUSED_TO_PLAYING:
623       break;
624     case GST_STATE_PLAYING_TO_PAUSED:
625       break;
626     case GST_STATE_PAUSED_TO_READY:
627       gst_buffer_unref (flxdec->frame);
628       flxdec->frame = NULL;
629       gst_buffer_unref (flxdec->delta);
630       flxdec->delta = NULL;
631       gst_bytestream_destroy (flxdec->bs);
632       break;
633     case GST_STATE_READY_TO_NULL:
634       break;
635   }
636   
637   parent_class->change_state (element);
638
639   return GST_STATE_SUCCESS;
640 }
641
642 static void 
643 gst_flxdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
644 {
645   GstFlxDec *flxdec;
646
647   /* it's not null if we got it, but it might not be ours */
648   g_return_if_fail(GST_IS_FLXDEC(object));
649   flxdec = GST_FLXDEC(object);
650
651   switch (prop_id) {
652     default:
653       break;
654   }
655 }
656
657 static void 
658 gst_flxdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 
659 {
660   GstFlxDec *flxdec;
661
662   /* it's not null if we got it, but it might not be ours */
663   g_return_if_fail(GST_IS_FLXDEC(object));
664   flxdec = GST_FLXDEC(object);
665
666   switch (prop_id) {
667     default:
668       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
669       break;
670   }
671 }
672
673 static gboolean
674 plugin_init (GModule *module, GstPlugin *plugin)
675 {
676   GstElementFactory *factory;
677   GstTypeFactory *type;
678
679   /* this filter needs the bytestream package */
680   if (!gst_library_load("gstbytestream")) {
681     gst_info("flxdec:: could not load support library: 'gstbytestream'\n");
682     return FALSE;
683   }
684
685   factory = gst_element_factory_new("flxdec", GST_TYPE_FLXDEC, &flxdec_details);
686   g_return_val_if_fail(factory != NULL, FALSE);
687
688   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (sink_factory));
689   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_video_factory));
690
691   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
692
693   type = gst_type_factory_new (&flxdec_definition);
694   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
695
696   return TRUE;
697 }
698
699 GstPluginDesc plugin_desc = {
700   GST_VERSION_MAJOR,
701   GST_VERSION_MINOR,
702   "flxdec",
703   plugin_init
704 };