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