tizen 2.0 init
[framework/multimedia/gst-plugins-good0.10.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  * SECTION:element-flxdec
21  *
22  * This element decodes fli/flc/flx-video into raw video
23  */
24 /*
25  * http://www.coolutils.com/Formats/FLI
26  * http://woodshole.er.usgs.gov/operations/modeling/flc.html
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 #include <string.h>
33
34 #include "flx_fmt.h"
35 #include "gstflxdec.h"
36 #include <gst/video/video.h>
37
38 #define JIFFIE  (GST_SECOND/70)
39
40 GST_DEBUG_CATEGORY_STATIC (flxdec_debug);
41 #define GST_CAT_DEFAULT flxdec_debug
42
43 /* input */
44 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
45     GST_PAD_SINK,
46     GST_PAD_ALWAYS,
47     GST_STATIC_CAPS ("video/x-fli")
48     );
49
50 /* output */
51 static GstStaticPadTemplate src_video_factory = GST_STATIC_PAD_TEMPLATE ("src",
52     GST_PAD_SRC,
53     GST_PAD_ALWAYS,
54     GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
55     );
56
57
58 static void gst_flxdec_class_init (GstFlxDecClass * klass);
59 static void gst_flxdec_base_init (GstFlxDecClass * klass);
60 static void gst_flxdec_init (GstFlxDec * flxdec);
61 static void gst_flxdec_dispose (GstFlxDec * flxdec);
62
63 static GstFlowReturn gst_flxdec_chain (GstPad * pad, GstBuffer * buf);
64
65 static GstStateChangeReturn gst_flxdec_change_state (GstElement * element,
66     GstStateChange transition);
67
68 static gboolean gst_flxdec_src_query_handler (GstPad * pad, GstQuery * query);
69 static gboolean gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event);
70 static gboolean gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event);
71
72 static void flx_decode_color (GstFlxDec *, guchar *, guchar *, gint);
73 static void flx_decode_brun (GstFlxDec *, guchar *, guchar *);
74 static void flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *);
75 static void flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *);
76
77 #define rndalign(off) ((off) + ((off) & 1))
78
79 static GstElementClass *parent_class = NULL;
80
81 GType
82 gst_flxdec_get_type (void)
83 {
84   static GType flxdec_type = 0;
85
86   if (!flxdec_type) {
87     static const GTypeInfo flxdec_info = {
88       sizeof (GstFlxDecClass),
89       (GBaseInitFunc) gst_flxdec_base_init,
90       NULL,
91       (GClassInitFunc) gst_flxdec_class_init,
92       NULL,
93       NULL,
94       sizeof (GstFlxDec),
95       0,
96       (GInstanceInitFunc) gst_flxdec_init,
97     };
98
99     flxdec_type =
100         g_type_register_static (GST_TYPE_ELEMENT, "GstFlxDec", &flxdec_info, 0);
101   }
102   return flxdec_type;
103 }
104
105 static void
106 gst_flxdec_base_init (GstFlxDecClass * klass)
107 {
108   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
109
110   gst_element_class_set_details_simple (gstelement_class, "FLX video decoder",
111       "Codec/Decoder/Video",
112       "FLC/FLI/FLX video decoder",
113       "Sepp Wijnands <mrrazz@garbage-coderz.net>, Zeeshan Ali <zeenix@gmail.com>");
114   gst_element_class_add_static_pad_template (gstelement_class,
115       &sink_factory);
116   gst_element_class_add_static_pad_template (gstelement_class,
117       &src_video_factory);
118 }
119
120 static void
121 gst_flxdec_class_init (GstFlxDecClass * klass)
122 {
123   GObjectClass *gobject_class;
124   GstElementClass *gstelement_class;
125
126   gobject_class = (GObjectClass *) klass;
127   gstelement_class = (GstElementClass *) klass;
128
129   parent_class = g_type_class_peek_parent (klass);
130
131   gobject_class->dispose = (GObjectFinalizeFunc) gst_flxdec_dispose;
132
133   GST_DEBUG_CATEGORY_INIT (flxdec_debug, "flxdec", 0, "FLX video decoder");
134
135   gstelement_class->change_state = gst_flxdec_change_state;
136 }
137
138 static void
139 gst_flxdec_init (GstFlxDec * flxdec)
140 {
141   flxdec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
142   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->sinkpad);
143   gst_pad_set_chain_function (flxdec->sinkpad, gst_flxdec_chain);
144   gst_pad_set_event_function (flxdec->sinkpad, gst_flxdec_sink_event_handler);
145
146   flxdec->srcpad = gst_pad_new_from_static_template (&src_video_factory, "src");
147   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->srcpad);
148   gst_pad_set_query_function (flxdec->srcpad, gst_flxdec_src_query_handler);
149   gst_pad_set_event_function (flxdec->srcpad, gst_flxdec_src_event_handler);
150
151   gst_pad_use_fixed_caps (flxdec->srcpad);
152
153   flxdec->frame = NULL;
154   flxdec->delta = NULL;
155
156   flxdec->adapter = gst_adapter_new ();
157 }
158
159 static void
160 gst_flxdec_dispose (GstFlxDec * flxdec)
161 {
162   if (flxdec->adapter) {
163     g_object_unref (flxdec->adapter);
164     flxdec->adapter = NULL;
165   }
166
167   G_OBJECT_CLASS (parent_class)->dispose ((GObject *) flxdec);
168 }
169
170 static gboolean
171 gst_flxdec_src_query_handler (GstPad * pad, GstQuery * query)
172 {
173   GstFlxDec *flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
174   gboolean ret = FALSE;
175
176   switch (GST_QUERY_TYPE (query)) {
177     case GST_QUERY_DURATION:
178     {
179       GstFormat format;
180
181       gst_query_parse_duration (query, &format, NULL);
182
183       if (format != GST_FORMAT_TIME)
184         goto done;
185
186       gst_query_set_duration (query, format, flxdec->duration);
187
188       ret = TRUE;
189     }
190     default:
191       break;
192   }
193 done:
194   gst_object_unref (flxdec);
195
196   return ret;
197 }
198
199 static gboolean
200 gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event)
201 {
202   GstFlxDec *flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
203   gboolean ret;
204
205   /* TODO: implement the seek and other event handling */
206
207   ret = gst_pad_push_event (flxdec->sinkpad, event);
208
209   gst_object_unref (flxdec);
210
211   return ret;
212 }
213
214 static gboolean
215 gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event)
216 {
217   GstFlxDec *flxdec;
218   gboolean ret;
219
220   flxdec = GST_FLXDEC (gst_pad_get_parent (pad));
221
222   ret = gst_pad_push_event (flxdec->srcpad, event);
223
224   gst_object_unref (flxdec);
225   return ret;
226 }
227
228 static void
229 flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data,
230     guchar * dest)
231 {
232   FlxFrameChunk *hdr;
233
234   g_return_if_fail (data != NULL);
235
236   while (count--) {
237     hdr = (FlxFrameChunk *) data;
238     FLX_FRAME_CHUNK_FIX_ENDIANNESS (hdr);
239     data += FlxFrameChunkSize;
240
241     switch (hdr->id) {
242       case FLX_COLOR64:
243         flx_decode_color (flxdec, data, dest, 2);
244         data += rndalign (hdr->size) - FlxFrameChunkSize;
245         break;
246
247       case FLX_COLOR256:
248         flx_decode_color (flxdec, data, dest, 0);
249         data += rndalign (hdr->size) - FlxFrameChunkSize;
250         break;
251
252       case FLX_BRUN:
253         flx_decode_brun (flxdec, data, dest);
254         data += rndalign (hdr->size) - FlxFrameChunkSize;
255         break;
256
257       case FLX_LC:
258         flx_decode_delta_fli (flxdec, data, dest);
259         data += rndalign (hdr->size) - FlxFrameChunkSize;
260         break;
261
262       case FLX_SS2:
263         flx_decode_delta_flc (flxdec, data, dest);
264         data += rndalign (hdr->size) - FlxFrameChunkSize;
265         break;
266
267       case FLX_BLACK:
268         memset (dest, 0, flxdec->size);
269         break;
270
271       case FLX_MINI:
272         data += rndalign (hdr->size) - FlxFrameChunkSize;
273         break;
274
275       default:
276         GST_WARNING ("Unimplented chunk type: 0x%02x size: %d - skipping",
277             hdr->id, hdr->size);
278         data += rndalign (hdr->size) - FlxFrameChunkSize;
279         break;
280     }
281   }
282 }
283
284
285 static void
286 flx_decode_color (GstFlxDec * flxdec, guchar * data, guchar * dest, gint scale)
287 {
288   guint packs, count, indx;
289
290   g_return_if_fail (flxdec != NULL);
291
292   packs = (data[0] + (data[1] << 8));
293
294   data += 2;
295   indx = 0;
296
297   GST_LOG ("GstFlxDec: cmap packs: %d", packs);
298   while (packs--) {
299     /* color map index + skip count */
300     indx += *data++;
301
302     /* number of rgb triplets */
303     count = *data++ & 0xff;
304     if (count == 0)
305       count = 256;
306
307     GST_LOG ("GstFlxDec: cmap count: %d (indx: %d)", count, indx);
308     flx_set_palette_vector (flxdec->converter, indx, count, data, scale);
309
310     data += (count * 3);
311   }
312 }
313
314 static void
315 flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest)
316 {
317   gulong count, lines, row;
318   guchar x;
319
320   g_return_if_fail (flxdec != NULL);
321
322   lines = flxdec->hdr.height;
323   while (lines--) {
324     /* packet count.  
325      * should not be used anymore, since the flc format can
326      * contain more then 255 RLE packets. we use the frame 
327      * width instead. 
328      */
329     data++;
330
331     row = flxdec->hdr.width;
332     while (row) {
333       count = *data++;
334
335       if (count > 0x7f) {
336         /* literal run */
337         count = 0x100 - count;
338         row -= count;
339
340         while (count--)
341           *dest++ = *data++;
342
343       } else {
344         /* replicate run */
345         row -= count;
346         x = *data++;
347
348         while (count--)
349           *dest++ = x;
350       }
351     }
352   }
353 }
354
355 static void
356 flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
357 {
358   gulong count, packets, lines, start_line;
359   guchar *start_p, x;
360
361   g_return_if_fail (flxdec != NULL);
362   g_return_if_fail (flxdec->delta != NULL);
363
364   /* use last frame for delta */
365   memcpy (dest, GST_BUFFER_DATA (flxdec->delta),
366       GST_BUFFER_SIZE (flxdec->delta));
367
368   start_line = (data[0] + (data[1] << 8));
369   lines = (data[2] + (data[3] << 8));
370   data += 4;
371
372   /* start position of delta */
373   dest += (flxdec->hdr.width * start_line);
374   start_p = dest;
375
376   while (lines--) {
377     /* packet count */
378     packets = *data++;
379
380     while (packets--) {
381       /* skip count */
382       dest += *data++;
383
384       /* RLE count */
385       count = *data++;
386
387       if (count > 0x7f) {
388         /* literal run */
389         count = 0x100 - count;
390         x = *data++;
391
392         while (count--)
393           *dest++ = x;
394
395       } else {
396         /* replicate run */
397         while (count--)
398           *dest++ = *data++;
399       }
400     }
401     start_p += flxdec->hdr.width;
402     dest = start_p;
403   }
404 }
405
406 static void
407 flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest)
408 {
409   gulong count, lines, start_l, opcode;
410   guchar *start_p;
411
412   g_return_if_fail (flxdec != NULL);
413   g_return_if_fail (flxdec->delta != NULL);
414
415   /* use last frame for delta */
416   memcpy (dest, GST_BUFFER_DATA (flxdec->delta),
417       GST_BUFFER_SIZE (flxdec->delta));
418
419   lines = (data[0] + (data[1] << 8));
420   data += 2;
421
422   start_p = dest;
423   start_l = lines;
424
425   while (lines) {
426     dest = start_p + (flxdec->hdr.width * (start_l - lines));
427
428     /* process opcode(s) */
429     while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) {
430       data += 2;
431       if ((opcode & 0xc000) == 0xc000) {
432         /* skip count */
433         start_l += (0x10000 - opcode);
434         dest += flxdec->hdr.width * (0x10000 - opcode);
435       } else {
436         /* last pixel */
437         dest += flxdec->hdr.width;
438         *dest++ = (opcode & 0xff);
439       }
440     }
441     data += 2;
442
443     /* last opcode is the packet count */
444     while (opcode--) {
445       /* skip count */
446       dest += *data++;
447
448       /* RLE count */
449       count = *data++;
450
451       if (count > 0x7f) {
452         /* replicate word run */
453         count = 0x100 - count;
454         while (count--) {
455           *dest++ = data[0];
456           *dest++ = data[1];
457         }
458         data += 2;
459       } else {
460         /* literal word run */
461         while (count--) {
462           *dest++ = *data++;
463           *dest++ = *data++;
464         }
465       }
466     }
467     lines--;
468   }
469 }
470
471 static GstFlowReturn
472 gst_flxdec_chain (GstPad * pad, GstBuffer * buf)
473 {
474   GstCaps *caps;
475   guint avail;
476   GstFlowReturn res = GST_FLOW_OK;
477
478   GstFlxDec *flxdec;
479   FlxHeader *flxh;
480
481   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
482   flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
483   g_return_val_if_fail (flxdec != NULL, GST_FLOW_ERROR);
484
485   gst_adapter_push (flxdec->adapter, buf);
486   avail = gst_adapter_available (flxdec->adapter);
487
488   if (flxdec->state == GST_FLXDEC_READ_HEADER) {
489     if (avail >= FlxHeaderSize) {
490       const guint8 *data = gst_adapter_peek (flxdec->adapter, FlxHeaderSize);
491
492       memcpy ((gchar *) & flxdec->hdr, data, FlxHeaderSize);
493       FLX_HDR_FIX_ENDIANNESS (&(flxdec->hdr));
494       gst_adapter_flush (flxdec->adapter, FlxHeaderSize);
495
496       flxh = &flxdec->hdr;
497
498       /* check header */
499       if (flxh->type != FLX_MAGICHDR_FLI &&
500           flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX)
501         goto wrong_type;
502
503       GST_LOG ("size      :  %d", flxh->size);
504       GST_LOG ("frames    :  %d", flxh->frames);
505       GST_LOG ("width     :  %d", flxh->width);
506       GST_LOG ("height    :  %d", flxh->height);
507       GST_LOG ("depth     :  %d", flxh->depth);
508       GST_LOG ("speed     :  %d", flxh->speed);
509
510       flxdec->next_time = 0;
511
512       if (flxh->type == FLX_MAGICHDR_FLI) {
513         flxdec->frame_time = JIFFIE * flxh->speed;
514       } else if (flxh->speed == 0) {
515         flxdec->frame_time = GST_SECOND / 70;
516       } else {
517         flxdec->frame_time = flxh->speed * GST_MSECOND;
518       }
519
520       flxdec->duration = flxh->frames * flxdec->frame_time;
521       GST_LOG ("duration   :  %" GST_TIME_FORMAT,
522           GST_TIME_ARGS (flxdec->duration));
523
524       caps = gst_caps_from_string (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN);
525       gst_caps_set_simple (caps,
526           "width", G_TYPE_INT, flxh->width,
527           "height", G_TYPE_INT, flxh->height,
528           "framerate", GST_TYPE_FRACTION, (gint) GST_MSECOND,
529           (gint) flxdec->frame_time / 1000, NULL);
530
531       gst_pad_set_caps (flxdec->srcpad, caps);
532       gst_caps_unref (caps);
533
534       if (flxh->depth <= 8)
535         flxdec->converter =
536             flx_colorspace_converter_new (flxh->width, flxh->height);
537
538       if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) {
539         GST_LOG ("(FLC) aspect_dx :  %d", flxh->aspect_dx);
540         GST_LOG ("(FLC) aspect_dy :  %d", flxh->aspect_dy);
541         GST_LOG ("(FLC) oframe1   :  0x%08x", flxh->oframe1);
542         GST_LOG ("(FLC) oframe2   :  0x%08x", flxh->oframe2);
543       }
544
545       flxdec->size = (flxh->width * flxh->height);
546
547       /* create delta and output frame */
548       flxdec->frame = gst_buffer_new ();
549       flxdec->delta = gst_buffer_new ();
550       GST_BUFFER_DATA (flxdec->frame) = g_malloc (flxdec->size);
551       GST_BUFFER_MALLOCDATA (flxdec->frame) = GST_BUFFER_DATA (flxdec->frame);
552       GST_BUFFER_SIZE (flxdec->frame) = flxdec->size;
553       GST_BUFFER_DATA (flxdec->delta) = g_malloc (flxdec->size);
554       GST_BUFFER_MALLOCDATA (flxdec->delta) = GST_BUFFER_DATA (flxdec->delta);
555       GST_BUFFER_SIZE (flxdec->delta) = flxdec->size;
556
557       flxdec->state = GST_FLXDEC_PLAYING;
558     }
559   } else if (flxdec->state == GST_FLXDEC_PLAYING) {
560     GstBuffer *out;
561
562     /* while we have enough data in the adapter */
563     while (avail >= FlxFrameChunkSize) {
564       FlxFrameChunk flxfh;
565       guchar *chunk;
566       const guint8 *data;
567
568       chunk = NULL;
569       data = gst_adapter_peek (flxdec->adapter, FlxFrameChunkSize);
570       memcpy (&flxfh, data, FlxFrameChunkSize);
571       FLX_FRAME_CHUNK_FIX_ENDIANNESS (&flxfh);
572
573       switch (flxfh.id) {
574         case FLX_FRAME_TYPE:
575           /* check if we have the complete frame */
576           if (avail < flxfh.size)
577             goto need_more_data;
578
579           /* flush header */
580           gst_adapter_flush (flxdec->adapter, FlxFrameChunkSize);
581
582           chunk = gst_adapter_take (flxdec->adapter,
583               flxfh.size - FlxFrameChunkSize);
584           FLX_FRAME_TYPE_FIX_ENDIANNESS ((FlxFrameType *) chunk);
585           if (((FlxFrameType *) chunk)->chunks == 0)
586             break;
587
588           /* create 32 bits output frame */
589           res = gst_pad_alloc_buffer_and_set_caps (flxdec->srcpad,
590               GST_BUFFER_OFFSET_NONE,
591               flxdec->size * 4, GST_PAD_CAPS (flxdec->srcpad), &out);
592           if (res != GST_FLOW_OK)
593             break;
594
595           /* decode chunks */
596           flx_decode_chunks (flxdec,
597               ((FlxFrameType *) chunk)->chunks,
598               chunk + FlxFrameTypeSize, GST_BUFFER_DATA (flxdec->frame));
599
600           /* save copy of the current frame for possible delta. */
601           memcpy (GST_BUFFER_DATA (flxdec->delta),
602               GST_BUFFER_DATA (flxdec->frame), GST_BUFFER_SIZE (flxdec->delta));
603
604           /* convert current frame. */
605           flx_colorspace_convert (flxdec->converter,
606               GST_BUFFER_DATA (flxdec->frame), GST_BUFFER_DATA (out));
607
608           GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
609           flxdec->next_time += flxdec->frame_time;
610
611           gst_pad_push (flxdec->srcpad, out);
612           break;
613       }
614
615       if (chunk)
616         g_free (chunk);
617
618       avail = gst_adapter_available (flxdec->adapter);
619     }
620   }
621 need_more_data:
622   gst_object_unref (flxdec);
623   return res;
624
625   /* ERRORS */
626 wrong_type:
627   {
628     GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL),
629         ("not a flx file (type %x)", flxh->type));
630     gst_object_unref (flxdec);
631     return GST_FLOW_ERROR;
632   }
633 }
634
635 static GstStateChangeReturn
636 gst_flxdec_change_state (GstElement * element, GstStateChange transition)
637 {
638   GstFlxDec *flxdec;
639   GstStateChangeReturn ret;
640
641   flxdec = GST_FLXDEC (element);
642
643   switch (transition) {
644     case GST_STATE_CHANGE_NULL_TO_READY:
645       break;
646     case GST_STATE_CHANGE_READY_TO_PAUSED:
647       gst_adapter_clear (flxdec->adapter);
648       flxdec->state = GST_FLXDEC_READ_HEADER;
649       break;
650     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
651       break;
652     default:
653       break;
654   }
655
656   ret = parent_class->change_state (element, transition);
657
658   switch (transition) {
659     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
660       break;
661     case GST_STATE_CHANGE_PAUSED_TO_READY:
662       if (flxdec->frame) {
663         gst_buffer_unref (flxdec->frame);
664         flxdec->frame = NULL;
665       }
666       if (flxdec->delta) {
667         gst_buffer_unref (flxdec->delta);
668         flxdec->delta = NULL;
669       }
670       break;
671     case GST_STATE_CHANGE_READY_TO_NULL:
672       break;
673     default:
674       break;
675   }
676   return ret;
677 }
678
679 static gboolean
680 plugin_init (GstPlugin * plugin)
681 {
682   return gst_element_register (plugin, "flxdec",
683       GST_RANK_PRIMARY, GST_TYPE_FLXDEC);
684 }
685
686 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
687     GST_VERSION_MINOR,
688     "flxdec",
689     "FLC/FLI/FLX video decoder",
690     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)