Now flxdec works on big-endian machines as well.
[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 GST_DEBUG_CATEGORY_STATIC (flxdec_debug);
32 #define GST_CAT_DEFAULT flxdec_debug
33
34 /* flx element information */
35 static GstElementDetails flxdec_details = {
36   "FLX Decoder",
37   "Codec/Decoder/Audio",
38   "FLX decoder",
39   "Sepp Wijnands <mrrazz@garbage-coderz.net>, Zeeshan Ali <zeenix@gmail.com>"
40 };
41
42 /* Flx signals and args */
43 enum
44 {
45   /* FILL ME */
46   LAST_SIGNAL
47 };
48
49 enum
50 {
51   ARG_0
52 };
53
54 /* input */
55 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS ("video/x-fli")
59     );
60
61 /* output */
62 static GstStaticPadTemplate src_video_factory = GST_STATIC_PAD_TEMPLATE ("src",
63     GST_PAD_SRC,
64     GST_PAD_ALWAYS,
65     GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
66     );
67
68
69 static void gst_flxdec_class_init (GstFlxDecClass * klass);
70 static void gst_flxdec_base_init (GstFlxDecClass * klass);
71 static void gst_flxdec_init (GstFlxDec * flxdec);
72
73 static GstFlowReturn gst_flxdec_chain (GstPad * pad, GstBuffer * buf);
74
75 static GstStateChangeReturn gst_flxdec_change_state (GstElement * element,
76     GstStateChange transition);
77
78 static gboolean gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event);
79 static gboolean gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event);
80
81 static void gst_flxdec_set_property (GObject * object, guint prop_id,
82     const GValue * value, GParamSpec * pspec);
83 static void gst_flxdec_get_property (GObject * object, guint prop_id,
84     GValue * value, GParamSpec * pspec);
85
86
87 static void flx_decode_color (GstFlxDec *, guchar *, guchar *, gint);
88 static void flx_decode_brun (GstFlxDec *, guchar *, guchar *);
89 static void flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *);
90 static void flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *);
91
92 #define rndalign(off) ((off) + ((off) % 2))
93
94 static GstElementClass *parent_class = NULL;
95
96 GType
97 gst_flxdec_get_type (void)
98 {
99   static GType flxdec_type = 0;
100
101   if (!flxdec_type) {
102     static const GTypeInfo flxdec_info = {
103       sizeof (GstFlxDecClass),
104       (GBaseInitFunc) gst_flxdec_base_init,
105       NULL,
106       (GClassInitFunc) gst_flxdec_class_init,
107       NULL,
108       NULL,
109       sizeof (GstFlxDec),
110       0,
111       (GInstanceInitFunc) gst_flxdec_init,
112     };
113
114     flxdec_type =
115         g_type_register_static (GST_TYPE_ELEMENT, "GstFlxDec", &flxdec_info, 0);
116   }
117   return flxdec_type;
118 }
119
120 static void
121 gst_flxdec_base_init (GstFlxDecClass * klass)
122 {
123   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
124
125   gst_element_class_set_details (gstelement_class, &flxdec_details);
126   gst_element_class_add_pad_template (gstelement_class,
127       gst_static_pad_template_get (&sink_factory));
128   gst_element_class_add_pad_template (gstelement_class,
129       gst_static_pad_template_get (&src_video_factory));
130 }
131
132 static void
133 gst_flxdec_class_init (GstFlxDecClass * klass)
134 {
135   GObjectClass *gobject_class;
136   GstElementClass *gstelement_class;
137
138   gobject_class = (GObjectClass *) klass;
139   gstelement_class = (GstElementClass *) klass;
140
141   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
142
143   GST_DEBUG_CATEGORY_INIT (flxdec_debug, "flxdec", 0, "FLX video decoder");
144
145   gobject_class->set_property = gst_flxdec_set_property;
146   gobject_class->get_property = gst_flxdec_get_property;
147
148   gstelement_class->change_state = gst_flxdec_change_state;
149 }
150
151 static void
152 gst_flxdec_init (GstFlxDec * flxdec)
153 {
154   flxdec->sinkpad =
155       gst_pad_new_from_template (gst_static_pad_template_get (&sink_factory),
156       "sink");
157   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->sinkpad);
158   gst_pad_set_chain_function (flxdec->sinkpad, gst_flxdec_chain);
159   gst_pad_set_event_function (flxdec->sinkpad, gst_flxdec_sink_event_handler);
160
161   flxdec->srcpad =
162       gst_pad_new_from_template (gst_static_pad_template_get
163       (&src_video_factory), "src");
164   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->srcpad);
165   gst_pad_set_event_function (flxdec->srcpad, gst_flxdec_src_event_handler);
166
167   gst_pad_use_fixed_caps (flxdec->srcpad);
168
169   flxdec->frame = NULL;
170   flxdec->delta = NULL;
171
172   flxdec->adapter = gst_adapter_new ();
173 }
174
175 static gboolean
176 gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event)
177 {
178   GstFlxDec *flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
179   gboolean ret;
180
181   /* TODO: implement the seek and other event handling */
182
183   ret = gst_pad_push_event (flxdec->sinkpad, event);
184
185   gst_object_unref (flxdec);
186
187   return ret;
188 }
189
190 static gboolean
191 gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event)
192 {
193   GstFlxDec *flxdec;
194   gboolean ret;
195
196   flxdec = GST_FLXDEC (gst_pad_get_parent (pad));
197
198   ret = gst_pad_push_event (flxdec->srcpad, event);
199
200   gst_object_unref (flxdec);
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)\n", 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, start_l;
335   guchar *start_p, x;
336
337   g_return_if_fail (flxdec != NULL);
338   g_return_if_fail (flxdec->delta != NULL);
339
340   /* use last frame for delta */
341   memcpy (dest, GST_BUFFER_DATA (flxdec->delta),
342       GST_BUFFER_SIZE (flxdec->delta));
343
344   start_line = (data[0] + (data[1] << 8));
345   lines = (data[2] + (data[3] << 8));
346   data += 4;
347
348   /* start position of delta */
349   dest += (flxdec->hdr.width * start_line);
350   start_p = dest;
351   start_l = lines;
352
353   while (lines--) {
354     /* packet count */
355     packets = *data++;
356
357     while (packets--) {
358       /* skip count */
359       dest += *data++;
360
361       /* RLE count */
362       count = *data++;
363
364       if (count > 0x7f) {
365         /* literal run */
366         count = 0x100 - count;
367         x = *data++;
368
369         while (count--)
370           *dest++ = x;
371
372       } else {
373         /* replicate run */
374         while (count--)
375           *dest++ = *data++;
376       }
377     }
378     start_p += flxdec->hdr.width;
379     dest = start_p;
380   }
381 }
382
383 static void
384 flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest)
385 {
386   gulong count, lines, start_l, opcode;
387   guchar *start_p;
388
389   g_return_if_fail (flxdec != NULL);
390   g_return_if_fail (flxdec->delta != NULL);
391
392   /* use last frame for delta */
393   memcpy (dest, GST_BUFFER_DATA (flxdec->delta),
394       GST_BUFFER_SIZE (flxdec->delta));
395
396   lines = (data[0] + (data[1] << 8));
397   data += 2;
398
399   start_p = dest;
400   start_l = lines;
401
402   while (lines) {
403     dest = start_p + (flxdec->hdr.width * (start_l - lines));
404
405     /* process opcode(s) */
406     while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) {
407       data += 2;
408       if ((opcode & 0xc000) == 0xc000) {
409         /* skip count */
410         start_l += (0x10000 - opcode);
411         dest += flxdec->hdr.width * (0x10000 - opcode);
412       } else {
413         /* last pixel */
414         dest += flxdec->hdr.width;
415         *dest++ = (opcode & 0xff);
416       }
417     }
418     data += 2;
419
420     /* last opcode is the packet count */
421     while (opcode--) {
422       /* skip count */
423       dest += *data++;
424
425       /* RLE count */
426       count = *data++;
427
428       if (count > 0x7f) {
429         /* replicate word run */
430         count = 0x100 - count;
431         while (count--) {
432           *dest++ = data[0];
433           *dest++ = data[1];
434         }
435         data += 2;
436       } else {
437         /* literal word run */
438         while (count--) {
439           *dest++ = *data++;
440           *dest++ = *data++;
441         }
442       }
443     }
444     lines--;
445   }
446 }
447
448 static GstFlowReturn
449 gst_flxdec_chain (GstPad * pad, GstBuffer * buf)
450 {
451   GstCaps *caps;
452   guint avail;
453   GstFlowReturn res = GST_FLOW_OK;
454
455   GstFlxDec *flxdec;
456   FlxHeader *flxh;
457
458   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
459   flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
460   g_return_val_if_fail (flxdec != NULL, GST_FLOW_ERROR);
461
462   gst_adapter_push (flxdec->adapter, buf);
463   avail = gst_adapter_available (flxdec->adapter);
464
465   if (flxdec->state == GST_FLXDEC_READ_HEADER) {
466     if (avail >= FlxHeaderSize) {
467       const guint8 *data = gst_adapter_peek (flxdec->adapter, FlxHeaderSize);
468
469       memcpy ((gchar *) & flxdec->hdr, data, FlxHeaderSize);
470       FLX_HDR_FIX_ENDIANNESS (&(flxdec->hdr));
471       gst_adapter_flush (flxdec->adapter, FlxHeaderSize);
472
473       flxh = &flxdec->hdr;
474
475       /* check header */
476       if (flxh->type != FLX_MAGICHDR_FLI &&
477           flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX) {
478         GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL),
479             ("not a flx file (type %x)\n", flxh->type));
480         return GST_FLOW_ERROR;
481       }
482
483
484       GST_LOG ("size      :  %d\n", flxh->size);
485       GST_LOG ("frames    :  %d\n", flxh->frames);
486       GST_LOG ("width     :  %d\n", flxh->width);
487       GST_LOG ("height    :  %d\n", flxh->height);
488       GST_LOG ("depth     :  %d\n", flxh->depth);
489       GST_LOG ("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       } else {
496         flxdec->frame_time = flxh->speed * GST_MSECOND;
497       }
498
499       caps = gst_caps_from_string (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN);
500       gst_caps_set_simple (caps,
501           "width", G_TYPE_INT, flxh->width,
502           "height", G_TYPE_INT, flxh->height,
503           "framerate", GST_TYPE_FRACTION, (gint) GST_MSECOND,
504           (gint) flxdec->frame_time / 1000, NULL);
505
506       gst_pad_set_caps (flxdec->srcpad, caps);
507       gst_caps_unref (caps);
508
509       if (flxh->depth <= 8)
510         flxdec->converter =
511             flx_colorspace_converter_new (flxh->width, flxh->height);
512
513       if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) {
514         GST_LOG ("(FLC) aspect_dx :  %d\n", flxh->aspect_dx);
515         GST_LOG ("(FLC) aspect_dy :  %d\n", flxh->aspect_dy);
516         GST_LOG ("(FLC) oframe1   :  0x%08x\n", flxh->oframe1);
517         GST_LOG ("(FLC) oframe2   :  0x%08x\n", flxh->oframe2);
518       }
519
520       flxdec->size = (flxh->width * flxh->height);
521
522       /* create delta and output frame */
523       flxdec->frame = gst_buffer_new ();
524       flxdec->delta = gst_buffer_new ();
525       GST_BUFFER_DATA (flxdec->frame) = g_malloc (flxdec->size);
526       GST_BUFFER_SIZE (flxdec->frame) = flxdec->size;
527       GST_BUFFER_DATA (flxdec->delta) = g_malloc (flxdec->size);
528       GST_BUFFER_SIZE (flxdec->delta) = flxdec->size;
529
530       flxdec->state = GST_FLXDEC_PLAYING;
531     }
532   } else if (flxdec->state == GST_FLXDEC_PLAYING) {
533     GstBuffer *out;
534
535     if (avail >= FlxFrameChunkSize) {
536       FlxFrameChunk flxfh;
537       guchar *chunk = NULL;
538       guint to_flush = 0;
539       const guint8 *data =
540           gst_adapter_peek (flxdec->adapter, FlxFrameChunkSize);
541       memcpy (&flxfh, data, FlxFrameChunkSize);
542       FLX_FRAME_CHUNK_FIX_ENDIANNESS (&flxfh);
543
544       switch (flxfh.id) {
545         case FLX_FRAME_TYPE:
546           if (avail < flxfh.size) {
547             break;
548           }
549
550           gst_adapter_flush (flxdec->adapter, FlxFrameChunkSize);
551           data = gst_adapter_peek (flxdec->adapter,
552               flxfh.size - FlxFrameChunkSize);
553           chunk = g_memdup (data, flxfh.size - FlxFrameChunkSize);
554           to_flush = flxfh.size - FlxFrameChunkSize;
555
556           FLX_FRAME_TYPE_FIX_ENDIANNESS ((FlxFrameType *) chunk);
557           if (((FlxFrameType *) chunk)->chunks == 0)
558             break;
559
560           /* create 32 bits output frame */
561           res = gst_pad_alloc_buffer_and_set_caps (flxdec->srcpad,
562               GST_BUFFER_OFFSET_NONE,
563               flxdec->size * 4, GST_PAD_CAPS (flxdec->srcpad), &out);
564
565           if (res != GST_FLOW_OK)
566             break;
567
568           /* decode chunks */
569           flx_decode_chunks (flxdec,
570               ((FlxFrameType *) chunk)->chunks,
571               chunk + FlxFrameTypeSize, GST_BUFFER_DATA (flxdec->frame));
572
573           /* save copy of the current frame for possible delta. */
574           memcpy (GST_BUFFER_DATA (flxdec->delta),
575               GST_BUFFER_DATA (flxdec->frame), GST_BUFFER_SIZE (flxdec->delta));
576
577           /* convert current frame. */
578           flx_colorspace_convert (flxdec->converter,
579               GST_BUFFER_DATA (flxdec->frame), GST_BUFFER_DATA (out));
580
581           GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
582           flxdec->next_time += flxdec->frame_time;
583
584           gst_pad_push (flxdec->srcpad, out);
585           break;
586       }
587
588       gst_adapter_flush (flxdec->adapter, to_flush);
589
590       if (chunk)
591         g_free (chunk);
592     }
593   }
594
595   return res;
596 }
597
598 static GstStateChangeReturn
599 gst_flxdec_change_state (GstElement * element, GstStateChange transition)
600 {
601   GstFlxDec *flxdec;
602   GstStateChangeReturn ret;
603
604   flxdec = GST_FLXDEC (element);
605
606   switch (transition) {
607     case GST_STATE_CHANGE_NULL_TO_READY:
608       break;
609     case GST_STATE_CHANGE_READY_TO_PAUSED:
610       gst_adapter_clear (flxdec->adapter);
611       flxdec->state = GST_FLXDEC_READ_HEADER;
612       break;
613     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
614       break;
615     default:
616       break;
617   }
618
619   ret = parent_class->change_state (element, transition);
620
621   switch (transition) {
622     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
623       break;
624     case GST_STATE_CHANGE_PAUSED_TO_READY:
625       if (flxdec->frame) {
626         gst_buffer_unref (flxdec->frame);
627         flxdec->frame = NULL;
628       }
629       if (flxdec->delta) {
630         gst_buffer_unref (flxdec->delta);
631         flxdec->delta = NULL;
632       }
633       break;
634     case GST_STATE_CHANGE_READY_TO_NULL:
635       break;
636     default:
637       break;
638   }
639   return ret;
640 }
641
642 static void
643 gst_flxdec_set_property (GObject * object, guint prop_id, const GValue * value,
644     GParamSpec * pspec)
645 {
646   GstFlxDec *flxdec;
647
648   g_return_if_fail (GST_IS_FLXDEC (object));
649   flxdec = GST_FLXDEC (object);
650
651   switch (prop_id) {
652     default:
653       break;
654   }
655 }
656
657 static void
658 gst_flxdec_get_property (GObject * object, guint prop_id, GValue * value,
659     GParamSpec * pspec)
660 {
661   GstFlxDec *flxdec;
662
663   g_return_if_fail (GST_IS_FLXDEC (object));
664   flxdec = GST_FLXDEC (object);
665
666   switch (prop_id) {
667     default:
668       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
669       break;
670   }
671 }
672
673 static gboolean
674 plugin_init (GstPlugin * plugin)
675 {
676   return gst_element_register (plugin, "flxdec",
677       GST_RANK_PRIMARY, GST_TYPE_FLXDEC);
678 }
679
680 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
681     GST_VERSION_MINOR,
682     "flxdec",
683     "FLX video decoder",
684     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)