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