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