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