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