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