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