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