/GstBuffer/GstData/ in the API where you can pass events. Fix the plugins to deal...
[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 static GstCaps* flxdec_type_find (GstByteStream *bs, gpointer private);
32
33 /* flx element information */
34 static GstElementDetails flxdec_details = {
35   "FLX Decoder",
36   "Codec/Audio/Decoder",
37   "LGPL",
38   "FLX decoder",
39   VERSION,
40   "Sepp Wijnands <mrrazz@garbage-coderz.net>",
41   "(C) 2001",
42 };
43
44 static GstTypeDefinition flxdec_definition = {
45   "flxdec_video/fli",
46   "video/fli",
47   ".flc .fli",
48   flxdec_type_find,
49 };
50
51 /* Flx signals and args */
52 enum {
53   /* FILL ME */
54   LAST_SIGNAL
55 };
56
57 enum {
58   ARG_0
59 };
60
61 /* input */
62 GST_PAD_TEMPLATE_FACTORY (sink_factory,
63   "sink",          
64   GST_PAD_SINK, 
65   GST_PAD_ALWAYS,
66   GST_CAPS_NEW (
67     "flxdec_sink",         
68     "video/x-fli",
69      NULL
70   )
71 )
72
73 /* output */
74 GST_PAD_TEMPLATE_FACTORY (src_video_factory,
75   "src",
76   GST_PAD_SRC,
77   GST_PAD_ALWAYS,
78   GST_CAPS_NEW (
79     "src_video",
80     "video/x-raw-rgb",
81         "bpp",        GST_PROPS_INT (32),
82         "depth",      GST_PROPS_INT (32),
83         "endianness", GST_PROPS_INT (G_BIG_ENDIAN),
84         "red_mask",   GST_PROPS_INT (R_MASK_32),
85         "green_mask", GST_PROPS_INT (G_MASK_32),
86         "blue_mask",  GST_PROPS_INT (B_MASK_32),
87         "width",      GST_PROPS_INT_RANGE(320, 1280), 
88         "height",     GST_PROPS_INT_RANGE(200, 1024),
89         "framerate",  GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT)
90   )
91 )
92
93
94 static void     gst_flxdec_class_init   (GstFlxDecClass *klass);
95 static void     gst_flxdec_init         (GstFlxDec *flxdec);
96
97 static void     gst_flxdec_loop         (GstElement *element);
98
99 static GstElementStateReturn 
100                 gst_flxdec_change_state (GstElement *element);
101
102 static void     gst_flxdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
103 static void     gst_flxdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
104
105
106 static void     flx_decode_color        (GstFlxDec *, guchar *, guchar *, gint);
107 static void     flx_decode_brun         (GstFlxDec *, guchar *, guchar *);
108 static void     flx_decode_delta_fli    (GstFlxDec *, guchar *, guchar *);
109 static void     flx_decode_delta_flc    (GstFlxDec *, guchar *, guchar *);
110
111 #define rndalign(off) ((off) + ((off) % 2))
112
113 static GstElementClass *parent_class = NULL;
114
115 static GstCaps* 
116 flxdec_type_find (GstByteStream *bs, gpointer private)
117 {
118   GstBuffer *buf = NULL;
119   GstCaps *new = NULL;
120
121   if (gst_bytestream_peek (bs, &buf, 134) == 134) {
122     guint8 *data = GST_BUFFER_DATA (buf);
123
124     /* check magic */
125     if ((data[4] == 0x11 || data[4] == 0x12 ||
126          data[4] == 0x30 || data[4] == 0x44) &&
127         data[5] == 0xaf) {
128       /* check the frame type of the first frame */
129       if ((data[132] == 0x00 || data[132] == 0xfa) && data[133] == 0xf1) {
130         GST_DEBUG ("GstFlxDec: found supported flx format");
131         new = gst_caps_new ("flxdec_type_find",
132                             "video/x-fli",
133                               NULL);
134       }
135     }
136   }
137
138   if (buf != NULL) {
139     gst_buffer_unref (buf);
140   }
141
142   return new;
143 }
144
145
146 GType
147 gst_flxdec_get_type(void) 
148 {
149   static GType flxdec_type = 0;
150
151   if (!flxdec_type) {
152     static const GTypeInfo flxdec_info = {
153       sizeof(GstFlxDecClass),      NULL,
154       NULL,
155       (GClassInitFunc)gst_flxdec_class_init,
156       NULL,
157       NULL,
158       sizeof(GstFlxDec),
159       0,
160       (GInstanceInitFunc)gst_flxdec_init,
161     };
162     flxdec_type = g_type_register_static(GST_TYPE_ELEMENT, "GstFlxDec", &flxdec_info, 0);
163   }
164   return flxdec_type;
165 }
166
167 static void 
168 gst_flxdec_class_init (GstFlxDecClass *klass) 
169 {
170   GObjectClass *gobject_class;
171   GstElementClass *gstelement_class;
172
173   gobject_class = (GObjectClass*)klass;
174   gstelement_class = (GstElementClass*)klass;
175
176   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
177
178   gobject_class->set_property = gst_flxdec_set_property;  
179   gobject_class->get_property = gst_flxdec_get_property;
180
181   gstelement_class->change_state = gst_flxdec_change_state;
182 }
183
184
185   
186 static void 
187 gst_flxdec_init(GstFlxDec *flxdec) 
188 {
189   flxdec->sinkpad = gst_pad_new_from_template (
190                   GST_PAD_TEMPLATE_GET (sink_factory), "sink");
191   gst_element_add_pad(GST_ELEMENT(flxdec),flxdec->sinkpad);
192   gst_element_set_loop_function(GST_ELEMENT(flxdec),gst_flxdec_loop);
193
194   flxdec->srcpad = gst_pad_new_from_template (
195                   GST_PAD_TEMPLATE_GET (src_video_factory), "src");
196   gst_element_add_pad(GST_ELEMENT(flxdec),flxdec->srcpad);
197
198   flxdec->bs = NULL;
199   flxdec->frame = NULL;
200   flxdec->delta = NULL;
201 }
202
203 static void
204 flx_decode_chunks (GstFlxDec *flxdec , gulong count, gchar *data, gchar *dest)
205 {
206   FlxFrameChunk  *hdr;
207
208   g_return_if_fail(data != NULL);
209
210   while (count--) {
211     hdr  = (FlxFrameChunk *) data;
212     data += FlxFrameChunkSize;
213
214     switch(hdr->id) 
215     {
216       case FLX_COLOR64:
217         flx_decode_color(flxdec, data, dest, 2);
218         data += rndalign(hdr->size) - FlxFrameChunkSize;
219         break;
220
221       case FLX_COLOR256:
222         flx_decode_color(flxdec, data, dest, 0);
223         data += rndalign(hdr->size) - FlxFrameChunkSize;
224         break;
225
226       case FLX_BRUN:
227         flx_decode_brun(flxdec, data, dest);
228         data += rndalign(hdr->size) - FlxFrameChunkSize;
229         break;
230
231       case FLX_LC:
232         flx_decode_delta_fli(flxdec, data, dest);
233         data += rndalign(hdr->size) - FlxFrameChunkSize;
234         break;
235
236       case FLX_SS2:
237         flx_decode_delta_flc(flxdec, data, dest);
238         data += rndalign(hdr->size) - FlxFrameChunkSize;
239         break;
240
241       case FLX_BLACK:
242         memset(dest, 0, flxdec->size);
243         break;
244
245       case FLX_MINI:
246         data += rndalign(hdr->size) - FlxFrameChunkSize;
247         break;
248
249       default:
250         g_print("GstFlxDec: Unimplented chunk type: 0x%02x size: %d\n",
251                  hdr->id, hdr->size);
252         g_print("GstFlxDec: Skipping...\n");
253         data += rndalign(hdr->size) - FlxFrameChunkSize;
254         break;
255     }
256   }
257 }
258
259
260 static void
261 flx_decode_color(GstFlxDec *flxdec, guchar *data, guchar *dest, gint scale)
262 {
263   guint packs, count, indx;
264
265   g_return_if_fail(flxdec != NULL);
266
267   packs = (data[0] + (data[1] << 8));
268
269   data += 2;
270   indx  = 0;
271
272   g_print("GstFlxDec: cmap packs: %d\n", packs);
273   while (packs--) {
274     /* color map index + skip count */
275     indx += *data++;
276
277     /* number of rgb triplets */
278     count = *data++ & 0xff;
279     if (count == 0)
280       count = 256;
281
282     g_print("GstFlxDec: cmap count: %d (indx: %d)\n", count, indx);
283     flx_set_palette_vector(flxdec->converter, indx, count, data, scale);
284
285     data += (count * 3);
286   }
287 }
288
289 static void 
290 flx_decode_brun(GstFlxDec *flxdec, guchar *data, guchar *dest)
291 {
292   gulong  count, lines, row;
293   guchar  x;
294   
295   g_return_if_fail(flxdec != NULL);
296
297   lines = flxdec->hdr.height;
298   while(lines--) {
299     /* packet count.  
300      * should not be used anymore, since the flc format can
301      * contain more then 255 RLE packets. we use the frame 
302      * width instead. 
303      */
304     data++;
305
306     row = flxdec->hdr.width;
307     while(row) {
308       count = *data++;
309
310       if (count > 0x7f) { 
311         /* literal run */
312         count = 0x100 - count;
313         row -= count;
314
315         while(count--) 
316           *dest++ = *data++;
317         
318       } else {
319         /* replicate run */
320         row -= count;
321         x = *data++;
322
323         while(count--) 
324           *dest++ = x;
325       }
326     }
327   }
328 }
329
330 static void 
331 flx_decode_delta_fli(GstFlxDec *flxdec, guchar *data, guchar *dest)
332 {
333   gulong  count, packets, lines, start_line, start_l;
334   guchar  *start_p,  x;
335   
336   g_return_if_fail(flxdec != NULL);
337   g_return_if_fail(flxdec->delta != NULL);
338
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
393   /* use last frame for delta */
394   memcpy(dest, GST_BUFFER_DATA(flxdec->delta), 
395          GST_BUFFER_SIZE(flxdec->delta));
396
397   lines      = (data[0] + (data[1] << 8));
398   data    += 2;
399
400   start_p    = dest;
401   start_l    = lines;
402
403   while (lines) {
404     dest = start_p + (flxdec->hdr.width * (start_l - lines));
405
406     /* process opcode(s) */
407     while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) {
408       data += 2;
409       if ((opcode & 0xc000) == 0xc000) {
410         /* skip count */
411         start_l += (0x10000 - opcode);  
412         dest    += flxdec->hdr.width * (0x10000 - opcode);
413       } else {
414         /* last pixel */
415         dest    += flxdec->hdr.width;
416         *dest++  = (opcode & 0xff);
417       }        
418     }
419     data += 2;
420
421     /* last opcode is the packet count */
422     while(opcode--) {
423       /* skip count */
424       dest += *data++;
425   
426       /* RLE count */
427       count = *data++;
428       
429       if (count > 0x7f) {
430         /* replicate word run */
431         count = 0x100 - count;
432         while (count--) {
433           *dest++ = data[0];
434           *dest++ = data[1];
435         }
436         data += 2;
437       } else {
438         /* literal word run */
439         while (count--) {
440           *dest++ = *data++;
441           *dest++ = *data++;
442         }
443       }
444     }
445     lines--;
446   }
447 }
448           
449 static GstBuffer*
450 flx_get_data(GstFlxDec *flxdec, gulong size)
451 {
452   GstBuffer *retbuf;
453   guint32 got_bytes;
454
455   g_return_val_if_fail (flxdec != NULL, NULL);
456
457   got_bytes = gst_bytestream_read (flxdec->bs, &retbuf, size);
458   if (got_bytes < size) {
459     GstEvent *event;
460     guint32 remaining;
461
462     gst_bytestream_get_status (flxdec->bs, &remaining, &event);
463     gst_pad_event_default (flxdec->sinkpad, event);
464   }
465
466   return retbuf;
467 }
468
469
470 static void 
471 gst_flxdec_loop (GstElement *element) 
472 {
473   GstBuffer  *buf;  
474   GstBuffer  *databuf;
475   guchar     *data, *chunk;
476
477   GstFlxDec         *flxdec;  
478   FlxHeader      *flxh;
479   FlxFrameChunk  *flxfh;
480   
481   g_return_if_fail (element != NULL);
482   g_return_if_fail (GST_IS_FLXDEC(element));
483
484   GST_DEBUG ("entering loop function");
485   
486   flxdec = GST_FLXDEC(element);
487
488   if (flxdec->state == GST_FLXDEC_READ_HEADER) {
489     databuf = flx_get_data(flxdec, FlxHeaderSize);
490
491     if (!databuf) {
492       g_print ("empty buffer\n");
493       return;
494     }
495
496     data = GST_BUFFER_DATA(databuf);
497
498     memcpy((char *) &flxdec->hdr, data, sizeof(FlxHeader));
499
500     gst_buffer_unref (databuf);
501
502     flxh = &flxdec->hdr;
503
504     /* check header */
505     if (flxh->type != FLX_MAGICHDR_FLI &&
506       flxh->type != FLX_MAGICHDR_FLC &&
507       flxh->type != FLX_MAGICHDR_FLX) {
508       gst_element_error (element, "not a flx file (type %d)\n", flxh->type);
509       return;
510     }
511   
512   
513     g_print("GstFlxDec:       size      :  %d\n", flxh->size);
514     g_print("GstFlxDec:       frames    :  %d\n", flxh->frames);
515     g_print("GstFlxDec:       width     :  %d\n", flxh->width);
516     g_print("GstFlxDec:       height    :  %d\n", flxh->height);
517     g_print("GstFlxDec:       depth     :  %d\n", flxh->depth);
518     g_print("GstFlxDec:       speed     :  %d\n", flxh->speed);
519
520     flxdec->next_time = 0;
521
522     if (flxh->type == FLX_MAGICHDR_FLI) {
523       flxdec->frame_time = JIFFIE * flxh->speed;
524     }
525     else {
526       flxdec->frame_time = flxh->speed * GST_MSECOND;
527     }
528     
529     gst_pad_try_set_caps (flxdec->srcpad,
530                 GST_CAPS_NEW (
531                   "src_video",
532                   "video/x-raw-rgb",
533                     "bpp",        GST_PROPS_INT (32),
534                     "depth",      GST_PROPS_INT (32),
535                     "endianness", GST_PROPS_INT (G_BIG_ENDIAN),
536                     "red_mask",   GST_PROPS_INT (R_MASK_32),
537                     "green_mask", GST_PROPS_INT (G_MASK_32),
538                     "blue_mask",  GST_PROPS_INT (B_MASK_32),
539                     "width",      GST_PROPS_INT (flxh->width), 
540                     "height",     GST_PROPS_INT (flxh->height),
541                     "framerate",  GST_PROPS_FLOAT (GST_SECOND/flxdec->frame_time)));
542
543     if (flxh->depth <= 8) 
544       flxdec->converter = flx_colorspace_converter_new(flxh->width, flxh->height);
545
546     if (flxh->type == FLX_MAGICHDR_FLC || 
547         flxh->type == FLX_MAGICHDR_FLX) {
548       g_print("GstFlxDec: (FLC) aspect_dx :  %d\n", flxh->aspect_dx);
549       g_print("GstFlxDec: (FLC) aspect_dy :  %d\n", flxh->aspect_dy);
550       g_print("GstFlxDec: (FLC) oframe1   :  0x%08x\n", flxh->oframe1);
551       g_print("GstFlxDec: (FLC) oframe2   :  0x%08x\n", flxh->oframe2);
552     }
553
554   
555     flxdec->size = (flxh->width * flxh->height);
556   
557     /* create delta and output frame */
558     flxdec->frame = gst_buffer_new();
559     flxdec->delta = gst_buffer_new();
560     GST_BUFFER_DATA(flxdec->frame) = g_malloc(flxdec->size);
561     GST_BUFFER_SIZE(flxdec->frame) = flxdec->size;
562     GST_BUFFER_DATA(flxdec->delta) = g_malloc(flxdec->size);
563     GST_BUFFER_SIZE(flxdec->delta) = flxdec->size;
564
565     flxdec->state = GST_FLXDEC_PLAYING;
566   }
567   else if (flxdec->state == GST_FLXDEC_PLAYING) {
568     GstBuffer *out;
569
570     databuf = flx_get_data(flxdec, FlxFrameChunkSize);
571     if (!databuf)
572       return;
573
574     flxfh = (FlxFrameChunk *) GST_BUFFER_DATA(databuf);
575     
576     switch(flxfh->id) {
577       case FLX_FRAME_TYPE:
578         buf = flx_get_data(flxdec, flxfh->size-FlxFrameChunkSize);
579  
580         chunk = GST_BUFFER_DATA(buf);
581  
582         if (((FlxFrameType *)chunk)->chunks == 0)
583           break;
584
585         /* create 32 bits output frame */
586         out = gst_buffer_new();
587         GST_BUFFER_DATA(out) = g_malloc(flxdec->size * 4);
588         GST_BUFFER_SIZE(out) = flxdec->size * 4;
589
590         /* decode chunks */
591         flx_decode_chunks(flxdec, 
592                          ((FlxFrameType *)chunk)->chunks, 
593                          GST_BUFFER_DATA(buf) + FlxFrameTypeSize,
594                          GST_BUFFER_DATA(flxdec->frame));
595  
596         /* destroy input buffer*/
597         gst_buffer_unref(buf);
598     
599         /* save copy of the current frame for possible delta. */
600         memcpy(GST_BUFFER_DATA(flxdec->delta), 
601                GST_BUFFER_DATA(flxdec->frame), 
602                GST_BUFFER_SIZE(flxdec->delta));
603
604         /* convert current frame. */
605         flx_colorspace_convert(flxdec->converter,
606              GST_BUFFER_DATA(flxdec->frame),
607              GST_BUFFER_DATA(out));
608
609         GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
610         flxdec->next_time += flxdec->frame_time;
611
612         gst_pad_push(flxdec->srcpad, GST_DATA (out));
613         
614         break;
615     }
616
617     /* destroy header buffer */
618     gst_buffer_unref(databuf);
619   }
620 }
621
622 static GstElementStateReturn 
623 gst_flxdec_change_state (GstElement *element)
624 {
625   GstFlxDec *flxdec;
626
627   flxdec = GST_FLXDEC(element);
628
629   switch (GST_STATE_TRANSITION (element)) {
630     case GST_STATE_NULL_TO_READY:
631       break;
632     case GST_STATE_READY_TO_PAUSED:
633       flxdec->bs = gst_bytestream_new (flxdec->sinkpad);
634       flxdec->state = GST_FLXDEC_READ_HEADER;
635       break;
636     case GST_STATE_PAUSED_TO_PLAYING:
637       break;
638     case GST_STATE_PLAYING_TO_PAUSED:
639       break;
640     case GST_STATE_PAUSED_TO_READY:
641       gst_buffer_unref (flxdec->frame);
642       flxdec->frame = NULL;
643       gst_buffer_unref (flxdec->delta);
644       flxdec->delta = NULL;
645       gst_bytestream_destroy (flxdec->bs);
646       break;
647     case GST_STATE_READY_TO_NULL:
648       break;
649   }
650   
651   parent_class->change_state (element);
652
653   return GST_STATE_SUCCESS;
654 }
655
656 static void 
657 gst_flxdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
658 {
659   GstFlxDec *flxdec;
660
661   /* it's not null if we got it, but it might not be ours */
662   g_return_if_fail(GST_IS_FLXDEC(object));
663   flxdec = GST_FLXDEC(object);
664
665   switch (prop_id) {
666     default:
667       break;
668   }
669 }
670
671 static void 
672 gst_flxdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 
673 {
674   GstFlxDec *flxdec;
675
676   /* it's not null if we got it, but it might not be ours */
677   g_return_if_fail(GST_IS_FLXDEC(object));
678   flxdec = GST_FLXDEC(object);
679
680   switch (prop_id) {
681     default:
682       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
683       break;
684   }
685 }
686
687 static gboolean
688 plugin_init (GModule *module, GstPlugin *plugin)
689 {
690   GstElementFactory *factory;
691   GstTypeFactory *type;
692
693   factory = gst_element_factory_new("flxdec", GST_TYPE_FLXDEC, &flxdec_details);
694   g_return_val_if_fail(factory != NULL, FALSE);
695   gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_PRIMARY);
696
697   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (sink_factory));
698   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_video_factory));
699
700   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
701
702   type = gst_type_factory_new (&flxdec_definition);
703   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
704
705   return TRUE;
706 }
707
708 GstPluginDesc plugin_desc = {
709   GST_VERSION_MAJOR,
710   GST_VERSION_MINOR,
711   "flxdec",
712   plugin_init
713 };