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