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