• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
3  * Copyright (C) <2016> Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 /**
21  * SECTION:element-flxdec
22  * @title: flxdec
23  *
24  * This element decodes fli/flc/flx-video into raw video
25  */
26 /*
27  * http://www.coolutils.com/Formats/FLI
28  * http://woodshole.er.usgs.gov/operations/modeling/flc.html
29  * http://www.compuphase.com/flic.htm
30  */
31 
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 #include <string.h>
36 
37 #include "flx_fmt.h"
38 #include "gstflxdec.h"
39 #include <gst/video/video.h>
40 
41 #define JIFFIE  (GST_SECOND/70)
42 
43 GST_DEBUG_CATEGORY_STATIC (flxdec_debug);
44 #define GST_CAT_DEFAULT flxdec_debug
45 
46 /* input */
47 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
48     GST_PAD_SINK,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS ("video/x-fli")
51     );
52 
53 #if G_BYTE_ORDER == G_BIG_ENDIAN
54 #define RGB_ORDER "xRGB"
55 #else
56 #define RGB_ORDER "BGRx"
57 #endif
58 
59 /* output */
60 static GstStaticPadTemplate src_video_factory = GST_STATIC_PAD_TEMPLATE ("src",
61     GST_PAD_SRC,
62     GST_PAD_ALWAYS,
63     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (RGB_ORDER))
64     );
65 
66 static void gst_flxdec_dispose (GstFlxDec * flxdec);
67 
68 static GstFlowReturn gst_flxdec_chain (GstPad * pad, GstObject * parent,
69     GstBuffer * buf);
70 static gboolean gst_flxdec_sink_event_handler (GstPad * pad,
71     GstObject * parent, GstEvent * event);
72 
73 static GstStateChangeReturn gst_flxdec_change_state (GstElement * element,
74     GstStateChange transition);
75 
76 static gboolean gst_flxdec_src_query_handler (GstPad * pad, GstObject * parent,
77     GstQuery * query);
78 
79 static gboolean flx_decode_color (GstFlxDec * flxdec, GstByteReader * reader,
80     GstByteWriter * writer, gint scale);
81 static gboolean flx_decode_brun (GstFlxDec * flxdec,
82     GstByteReader * reader, GstByteWriter * writer);
83 static gboolean flx_decode_delta_fli (GstFlxDec * flxdec,
84     GstByteReader * reader, GstByteWriter * writer);
85 static gboolean flx_decode_delta_flc (GstFlxDec * flxdec,
86     GstByteReader * reader, GstByteWriter * writer);
87 
88 #define rndalign(off) ((off) + ((off) & 1))
89 
90 #define gst_flxdec_parent_class parent_class
91 G_DEFINE_TYPE (GstFlxDec, gst_flxdec, GST_TYPE_ELEMENT);
92 GST_ELEMENT_REGISTER_DEFINE (flxdec, "flxdec",
93     GST_RANK_PRIMARY, GST_TYPE_FLXDEC);
94 
95 static void
gst_flxdec_class_init(GstFlxDecClass * klass)96 gst_flxdec_class_init (GstFlxDecClass * klass)
97 {
98   GObjectClass *gobject_class;
99   GstElementClass *gstelement_class;
100 
101   gobject_class = (GObjectClass *) klass;
102   gstelement_class = (GstElementClass *) klass;
103 
104   parent_class = g_type_class_peek_parent (klass);
105 
106   gobject_class->dispose = (GObjectFinalizeFunc) gst_flxdec_dispose;
107 
108   GST_DEBUG_CATEGORY_INIT (flxdec_debug, "flxdec", 0, "FLX video decoder");
109 
110   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_flxdec_change_state);
111 
112   gst_element_class_set_static_metadata (gstelement_class, "FLX video decoder",
113       "Codec/Decoder/Video",
114       "FLC/FLI/FLX video decoder",
115       "Sepp Wijnands <mrrazz@garbage-coderz.net>, Zeeshan Ali <zeenix@gmail.com>");
116   gst_element_class_add_pad_template (gstelement_class,
117       gst_static_pad_template_get (&sink_factory));
118   gst_element_class_add_pad_template (gstelement_class,
119       gst_static_pad_template_get (&src_video_factory));
120 }
121 
122 static void
gst_flxdec_init(GstFlxDec * flxdec)123 gst_flxdec_init (GstFlxDec * flxdec)
124 {
125   flxdec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
126   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->sinkpad);
127   gst_pad_set_chain_function (flxdec->sinkpad,
128       GST_DEBUG_FUNCPTR (gst_flxdec_chain));
129   gst_pad_set_event_function (flxdec->sinkpad,
130       GST_DEBUG_FUNCPTR (gst_flxdec_sink_event_handler));
131 
132   flxdec->srcpad = gst_pad_new_from_static_template (&src_video_factory, "src");
133   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->srcpad);
134   gst_pad_set_query_function (flxdec->srcpad,
135       GST_DEBUG_FUNCPTR (gst_flxdec_src_query_handler));
136 
137   gst_pad_use_fixed_caps (flxdec->srcpad);
138 
139   flxdec->adapter = gst_adapter_new ();
140 }
141 
142 static void
gst_flxdec_dispose(GstFlxDec * flxdec)143 gst_flxdec_dispose (GstFlxDec * flxdec)
144 {
145   if (flxdec->adapter) {
146     g_object_unref (flxdec->adapter);
147     flxdec->adapter = NULL;
148   }
149 
150   G_OBJECT_CLASS (parent_class)->dispose ((GObject *) flxdec);
151 }
152 
153 static gboolean
gst_flxdec_src_query_handler(GstPad * pad,GstObject * parent,GstQuery * query)154 gst_flxdec_src_query_handler (GstPad * pad, GstObject * parent,
155     GstQuery * query)
156 {
157   GstFlxDec *flxdec = (GstFlxDec *) parent;
158   gboolean ret = FALSE;
159 
160   switch (GST_QUERY_TYPE (query)) {
161     case GST_QUERY_DURATION:
162     {
163       GstFormat format;
164 
165       gst_query_parse_duration (query, &format, NULL);
166 
167       if (format != GST_FORMAT_TIME)
168         goto done;
169 
170       gst_query_set_duration (query, format, flxdec->duration);
171 
172       ret = TRUE;
173     }
174     default:
175       break;
176   }
177 done:
178   if (!ret)
179     ret = gst_pad_query_default (pad, parent, query);
180 
181   return ret;
182 }
183 
184 static gboolean
gst_flxdec_sink_event_handler(GstPad * pad,GstObject * parent,GstEvent * event)185 gst_flxdec_sink_event_handler (GstPad * pad, GstObject * parent,
186     GstEvent * event)
187 {
188   GstFlxDec *flxdec;
189   gboolean ret;
190 
191   flxdec = GST_FLXDEC (parent);
192 
193   switch (GST_EVENT_TYPE (event)) {
194     case GST_EVENT_SEGMENT:
195     {
196       gst_event_copy_segment (event, &flxdec->segment);
197       if (flxdec->segment.format != GST_FORMAT_TIME) {
198         GST_DEBUG_OBJECT (flxdec, "generating TIME segment");
199         gst_segment_init (&flxdec->segment, GST_FORMAT_TIME);
200         gst_event_unref (event);
201         event = gst_event_new_segment (&flxdec->segment);
202       }
203 
204       if (gst_pad_has_current_caps (flxdec->srcpad)) {
205         ret = gst_pad_event_default (pad, parent, event);
206       } else {
207         flxdec->need_segment = TRUE;
208         gst_event_unref (event);
209         ret = TRUE;
210       }
211       break;
212     }
213     case GST_EVENT_FLUSH_STOP:
214       gst_segment_init (&flxdec->segment, GST_FORMAT_UNDEFINED);
215       ret = gst_pad_event_default (pad, parent, event);
216       break;
217     default:
218       ret = gst_pad_event_default (pad, parent, event);
219       break;
220   }
221 
222   return ret;
223 }
224 
225 static gboolean
flx_decode_chunks(GstFlxDec * flxdec,gulong n_chunks,GstByteReader * reader,GstByteWriter * writer)226 flx_decode_chunks (GstFlxDec * flxdec, gulong n_chunks, GstByteReader * reader,
227     GstByteWriter * writer)
228 {
229   gboolean ret = TRUE;
230 
231   while (n_chunks--) {
232     GstByteReader chunk;
233     guint32 size;
234     guint16 type;
235 
236     if (!gst_byte_reader_get_uint32_le (reader, &size))
237       goto parse_error;
238     if (!gst_byte_reader_get_uint16_le (reader, &type))
239       goto parse_error;
240     GST_LOG_OBJECT (flxdec, "chunk has type 0x%02x size %d", type, size);
241 
242     if (!gst_byte_reader_get_sub_reader (reader, &chunk,
243             size - FlxFrameChunkSize)) {
244       GST_ERROR_OBJECT (flxdec, "Incorrect size in the chunk header");
245       goto error;
246     }
247 
248     switch (type) {
249       case FLX_COLOR64:
250         ret = flx_decode_color (flxdec, &chunk, writer, 2);
251         break;
252 
253       case FLX_COLOR256:
254         ret = flx_decode_color (flxdec, &chunk, writer, 0);
255         break;
256 
257       case FLX_BRUN:
258         ret = flx_decode_brun (flxdec, &chunk, writer);
259         break;
260 
261       case FLX_LC:
262         ret = flx_decode_delta_fli (flxdec, &chunk, writer);
263         break;
264 
265       case FLX_SS2:
266         ret = flx_decode_delta_flc (flxdec, &chunk, writer);
267         break;
268 
269       case FLX_BLACK:
270         ret = gst_byte_writer_fill (writer, 0, flxdec->size);
271         break;
272 
273       case FLX_MINI:
274         break;
275 
276       default:
277         GST_WARNING ("Unimplemented chunk type: 0x%02x size: %d - skipping",
278             type, size);
279         break;
280     }
281 
282     if (!ret)
283       break;
284   }
285 
286   return ret;
287 
288 parse_error:
289   GST_ERROR_OBJECT (flxdec, "Failed to decode chunk");
290 error:
291   return FALSE;
292 }
293 
294 
295 static gboolean
flx_decode_color(GstFlxDec * flxdec,GstByteReader * reader,GstByteWriter * writer,gint scale)296 flx_decode_color (GstFlxDec * flxdec, GstByteReader * reader,
297     GstByteWriter * writer, gint scale)
298 {
299   guint8 count, indx;
300   guint16 packs;
301 
302   if (!gst_byte_reader_get_uint16_le (reader, &packs))
303     goto error;
304   indx = 0;
305 
306   GST_LOG ("GstFlxDec: cmap packs: %d", (guint) packs);
307   while (packs--) {
308     const guint8 *data;
309     guint16 actual_count;
310 
311     /* color map index + skip count */
312     if (!gst_byte_reader_get_uint8 (reader, &indx))
313       goto error;
314 
315     /* number of rgb triplets */
316     if (!gst_byte_reader_get_uint8 (reader, &count))
317       goto error;
318 
319     actual_count = count == 0 ? 256 : count;
320 
321     if (!gst_byte_reader_get_data (reader, count * 3, &data))
322       goto error;
323 
324     GST_LOG_OBJECT (flxdec, "cmap count: %d (indx: %d)", actual_count, indx);
325     flx_set_palette_vector (flxdec->converter, indx, actual_count,
326         (guchar *) data, scale);
327   }
328 
329   return TRUE;
330 
331 error:
332   GST_ERROR_OBJECT (flxdec, "Error decoding color palette");
333   return FALSE;
334 }
335 
336 static gboolean
flx_decode_brun(GstFlxDec * flxdec,GstByteReader * reader,GstByteWriter * writer)337 flx_decode_brun (GstFlxDec * flxdec, GstByteReader * reader,
338     GstByteWriter * writer)
339 {
340   gulong lines, row;
341 
342   g_return_val_if_fail (flxdec != NULL, FALSE);
343 
344   lines = flxdec->hdr.height;
345   while (lines--) {
346     /* packet count.
347      * should not be used anymore, since the flc format can
348      * contain more then 255 RLE packets. we use the frame
349      * width instead.
350      */
351     if (!gst_byte_reader_skip (reader, 1))
352       goto error;
353 
354     row = flxdec->hdr.width;
355     while (row) {
356       gint8 count;
357 
358       if (!gst_byte_reader_get_int8 (reader, &count))
359         goto error;
360 
361       if (count <= 0) {
362         const guint8 *data;
363 
364         /* literal run */
365         count = ABS (count);
366 
367         GST_LOG_OBJECT (flxdec, "have literal run of size %d", count);
368 
369         if (count > row) {
370           GST_ERROR_OBJECT (flxdec, "Invalid BRUN line detected. "
371               "bytes to write exceeds the end of the row");
372           return FALSE;
373         }
374         row -= count;
375 
376         if (!gst_byte_reader_get_data (reader, count, &data))
377           goto error;
378         if (!gst_byte_writer_put_data (writer, data, count))
379           goto error;
380       } else {
381         guint8 x;
382 
383         GST_LOG_OBJECT (flxdec, "have replicate run of size %d", count);
384 
385         if (count > row) {
386           GST_ERROR_OBJECT (flxdec, "Invalid BRUN packet detected."
387               "bytes to write exceeds the end of the row");
388           return FALSE;
389         }
390 
391         /* replicate run */
392         row -= count;
393 
394         if (!gst_byte_reader_get_uint8 (reader, &x))
395           goto error;
396         if (!gst_byte_writer_fill (writer, x, count))
397           goto error;
398       }
399     }
400   }
401 
402   return TRUE;
403 
404 error:
405   GST_ERROR_OBJECT (flxdec, "Failed to decode BRUN packet");
406   return FALSE;
407 }
408 
409 static gboolean
flx_decode_delta_fli(GstFlxDec * flxdec,GstByteReader * reader,GstByteWriter * writer)410 flx_decode_delta_fli (GstFlxDec * flxdec, GstByteReader * reader,
411     GstByteWriter * writer)
412 {
413   guint16 start_line, lines;
414   guint line_start_i;
415 
416   g_return_val_if_fail (flxdec != NULL, FALSE);
417   g_return_val_if_fail (flxdec->delta_data != NULL, FALSE);
418 
419   /* use last frame for delta */
420   if (!gst_byte_writer_put_data (writer, flxdec->delta_data, flxdec->size))
421     goto error;
422 
423   if (!gst_byte_reader_get_uint16_le (reader, &start_line))
424     goto error;
425   if (!gst_byte_reader_get_uint16_le (reader, &lines))
426     goto error;
427   GST_LOG_OBJECT (flxdec, "height %d start line %d line count %d",
428       flxdec->hdr.height, start_line, lines);
429 
430   if (start_line + lines > flxdec->hdr.height) {
431     GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. too many lines.");
432     return FALSE;
433   }
434 
435   line_start_i = flxdec->hdr.width * start_line;
436   if (!gst_byte_writer_set_pos (writer, line_start_i))
437     goto error;
438 
439   while (lines--) {
440     guint8 packets;
441 
442     /* packet count */
443     if (!gst_byte_reader_get_uint8 (reader, &packets))
444       goto error;
445     GST_LOG_OBJECT (flxdec, "have %d packets", packets);
446 
447     while (packets--) {
448       /* skip count */
449       guint8 skip;
450       gint8 count;
451       if (!gst_byte_reader_get_uint8 (reader, &skip))
452         goto error;
453 
454       /* skip bytes */
455       if (!gst_byte_writer_set_pos (writer,
456               gst_byte_writer_get_pos (writer) + skip))
457         goto error;
458 
459       /* RLE count */
460       if (!gst_byte_reader_get_int8 (reader, &count))
461         goto error;
462 
463       if (count < 0) {
464         guint8 x;
465 
466         /* literal run */
467         count = ABS (count);
468         GST_LOG_OBJECT (flxdec, "have literal run of size %d at offset %d",
469             count, skip);
470 
471         if (skip + count > flxdec->hdr.width) {
472           GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. "
473               "line too long.");
474           return FALSE;
475         }
476 
477         if (!gst_byte_reader_get_uint8 (reader, &x))
478           goto error;
479         if (!gst_byte_writer_fill (writer, x, count))
480           goto error;
481       } else {
482         const guint8 *data;
483 
484         GST_LOG_OBJECT (flxdec, "have replicate run of size %d at offset %d",
485             count, skip);
486 
487         if (skip + count > flxdec->hdr.width) {
488           GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. "
489               "line too long.");
490           return FALSE;
491         }
492 
493         /* replicate run */
494         if (!gst_byte_reader_get_data (reader, count, &data))
495           goto error;
496         if (!gst_byte_writer_put_data (writer, data, count))
497           goto error;
498       }
499     }
500     line_start_i += flxdec->hdr.width;
501     if (!gst_byte_writer_set_pos (writer, line_start_i))
502       goto error;
503   }
504 
505   return TRUE;
506 
507 error:
508   GST_ERROR_OBJECT (flxdec, "Failed to decode FLI packet");
509   return FALSE;
510 }
511 
512 static gboolean
flx_decode_delta_flc(GstFlxDec * flxdec,GstByteReader * reader,GstByteWriter * writer)513 flx_decode_delta_flc (GstFlxDec * flxdec, GstByteReader * reader,
514     GstByteWriter * writer)
515 {
516   guint16 lines, start_l;
517 
518   g_return_val_if_fail (flxdec != NULL, FALSE);
519   g_return_val_if_fail (flxdec->delta_data != NULL, FALSE);
520 
521   /* use last frame for delta */
522   if (!gst_byte_writer_put_data (writer, flxdec->delta_data, flxdec->size))
523     goto error;
524   if (!gst_byte_reader_get_uint16_le (reader, &lines))
525     goto error;
526 
527   if (lines > flxdec->hdr.height) {
528     GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. too many lines.");
529     return FALSE;
530   }
531 
532   start_l = lines;
533 
534   while (lines) {
535     guint16 opcode;
536 
537     if (!gst_byte_writer_set_pos (writer,
538             flxdec->hdr.width * (start_l - lines)))
539       goto error;
540 
541     /* process opcode(s) */
542     while (TRUE) {
543       if (!gst_byte_reader_get_uint16_le (reader, &opcode))
544         goto error;
545       if ((opcode & 0xc000) == 0)
546         break;
547 
548       if ((opcode & 0xc000) == 0xc000) {
549         /* line skip count */
550         gulong skip = (0x10000 - opcode);
551         if (skip > flxdec->hdr.height) {
552           GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. "
553               "skip line count too big.");
554           return FALSE;
555         }
556         start_l += skip;
557         if (!gst_byte_writer_set_pos (writer,
558                 gst_byte_writer_get_pos (writer) + flxdec->hdr.width * skip))
559           goto error;
560       } else {
561         /* last pixel */
562         if (!gst_byte_writer_set_pos (writer,
563                 gst_byte_writer_get_pos (writer) + flxdec->hdr.width))
564           goto error;
565         if (!gst_byte_writer_put_uint8 (writer, opcode & 0xff))
566           goto error;
567       }
568     }
569 
570     /* last opcode is the packet count */
571     GST_LOG_OBJECT (flxdec, "have %d packets", opcode);
572     while (opcode--) {
573       /* skip count */
574       guint8 skip;
575       gint8 count;
576 
577       if (!gst_byte_reader_get_uint8 (reader, &skip))
578         goto error;
579       if (!gst_byte_writer_set_pos (writer,
580               gst_byte_writer_get_pos (writer) + skip))
581         goto error;
582 
583       /* RLE count */
584       if (!gst_byte_reader_get_int8 (reader, &count))
585         goto error;
586 
587       if (count < 0) {
588         guint16 x;
589 
590         /* replicate word run */
591         count = ABS (count);
592 
593         GST_LOG_OBJECT (flxdec, "have replicate run of size %d at offset %d",
594             count, skip);
595 
596         if (skip + count > flxdec->hdr.width) {
597           GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. "
598               "line too long.");
599           return FALSE;
600         }
601 
602         if (!gst_byte_reader_get_uint16_le (reader, &x))
603           goto error;
604 
605         while (count--) {
606           if (!gst_byte_writer_put_uint16_le (writer, x)) {
607             goto error;
608           }
609         }
610       } else {
611         GST_LOG_OBJECT (flxdec, "have literal run of size %d at offset %d",
612             count, skip);
613 
614         if (skip + count > flxdec->hdr.width) {
615           GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. "
616               "line too long.");
617           return FALSE;
618         }
619 
620         while (count--) {
621           guint16 x;
622 
623           if (!gst_byte_reader_get_uint16_le (reader, &x))
624             goto error;
625           if (!gst_byte_writer_put_uint16_le (writer, x))
626             goto error;
627         }
628       }
629     }
630     lines--;
631   }
632 
633   return TRUE;
634 
635 error:
636   GST_ERROR_OBJECT (flxdec, "Failed to decode FLI packet");
637   return FALSE;
638 }
639 
640 static gboolean
_read_flx_header(GstFlxDec * flxdec,GstByteReader * reader,FlxHeader * flxh)641 _read_flx_header (GstFlxDec * flxdec, GstByteReader * reader, FlxHeader * flxh)
642 {
643   memset (flxh, 0, sizeof (*flxh));
644 
645   if (!gst_byte_reader_get_uint32_le (reader, &flxh->size))
646     goto error;
647   if (flxh->size < FlxHeaderSize) {
648     GST_ERROR_OBJECT (flxdec, "Invalid file size in the header");
649     return FALSE;
650   }
651 
652   if (!gst_byte_reader_get_uint16_le (reader, &flxh->type))
653     goto error;
654   if (!gst_byte_reader_get_uint16_le (reader, &flxh->frames))
655     goto error;
656   if (!gst_byte_reader_get_uint16_le (reader, &flxh->width))
657     goto error;
658   if (!gst_byte_reader_get_uint16_le (reader, &flxh->height))
659     goto error;
660   if (!gst_byte_reader_get_uint16_le (reader, &flxh->depth))
661     goto error;
662   if (!gst_byte_reader_get_uint16_le (reader, &flxh->flags))
663     goto error;
664   if (!gst_byte_reader_get_uint32_le (reader, &flxh->speed))
665     goto error;
666   if (!gst_byte_reader_skip (reader, 2))        /* reserved */
667     goto error;
668   /* FLC */
669   if (!gst_byte_reader_get_uint32_le (reader, &flxh->created))
670     goto error;
671   if (!gst_byte_reader_get_uint32_le (reader, &flxh->creator))
672     goto error;
673   if (!gst_byte_reader_get_uint32_le (reader, &flxh->updated))
674     goto error;
675   if (!gst_byte_reader_get_uint32_le (reader, &flxh->updater))
676     goto error;
677   if (!gst_byte_reader_get_uint16_le (reader, &flxh->aspect_dx))
678     goto error;
679   if (!gst_byte_reader_get_uint16_le (reader, &flxh->aspect_dy))
680     goto error;
681   /* EGI */
682   if (!gst_byte_reader_get_uint16_le (reader, &flxh->ext_flags))
683     goto error;
684   if (!gst_byte_reader_get_uint16_le (reader, &flxh->keyframes))
685     goto error;
686   if (!gst_byte_reader_get_uint16_le (reader, &flxh->totalframes))
687     goto error;
688   if (!gst_byte_reader_get_uint32_le (reader, &flxh->req_memory))
689     goto error;
690   if (!gst_byte_reader_get_uint16_le (reader, &flxh->max_regions))
691     goto error;
692   if (!gst_byte_reader_get_uint16_le (reader, &flxh->transp_num))
693     goto error;
694   if (!gst_byte_reader_skip (reader, 24))       /* reserved */
695     goto error;
696   /* FLC */
697   if (!gst_byte_reader_get_uint32_le (reader, &flxh->oframe1))
698     goto error;
699   if (!gst_byte_reader_get_uint32_le (reader, &flxh->oframe2))
700     goto error;
701   if (!gst_byte_reader_skip (reader, 40))       /* reserved */
702     goto error;
703 
704   return TRUE;
705 
706 error:
707   GST_ERROR_OBJECT (flxdec, "Error reading file header");
708   return FALSE;
709 }
710 
711 static GstFlowReturn
gst_flxdec_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)712 gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
713 {
714   GstByteReader reader;
715   GstBuffer *input;
716   GstMapInfo map_info;
717   GstCaps *caps;
718   guint available;
719   GstFlowReturn res = GST_FLOW_OK;
720 
721   GstFlxDec *flxdec;
722   FlxHeader *flxh;
723 
724   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
725   flxdec = (GstFlxDec *) parent;
726   g_return_val_if_fail (flxdec != NULL, GST_FLOW_ERROR);
727 
728   gst_adapter_push (flxdec->adapter, buf);
729   available = gst_adapter_available (flxdec->adapter);
730   input = gst_adapter_get_buffer (flxdec->adapter, available);
731   if (!gst_buffer_map (input, &map_info, GST_MAP_READ)) {
732     GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
733         ("%s", "Failed to map buffer"), (NULL));
734     goto error;
735   }
736   gst_byte_reader_init (&reader, map_info.data, map_info.size);
737 
738   if (flxdec->state == GST_FLXDEC_READ_HEADER) {
739     if (available >= FlxHeaderSize) {
740       GstByteReader header;
741       GstCaps *templ;
742 
743       if (!gst_byte_reader_get_sub_reader (&reader, &header, FlxHeaderSize)) {
744         GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
745             ("%s", "Could not read header"), (NULL));
746         goto unmap_input_error;
747       }
748       gst_adapter_flush (flxdec->adapter, FlxHeaderSize);
749       available -= FlxHeaderSize;
750 
751       if (!_read_flx_header (flxdec, &header, &flxdec->hdr)) {
752         GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
753             ("%s", "Failed to parse header"), (NULL));
754         goto unmap_input_error;
755       }
756 
757       flxh = &flxdec->hdr;
758 
759       /* check header */
760       if (flxh->type != FLX_MAGICHDR_FLI &&
761           flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX) {
762         GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL),
763             ("not a flx file (type %x)", flxh->type));
764         goto unmap_input_error;
765       }
766 
767       GST_INFO_OBJECT (flxdec, "size      :  %d", flxh->size);
768       GST_INFO_OBJECT (flxdec, "frames    :  %d", flxh->frames);
769       GST_INFO_OBJECT (flxdec, "width     :  %d", flxh->width);
770       GST_INFO_OBJECT (flxdec, "height    :  %d", flxh->height);
771       GST_INFO_OBJECT (flxdec, "depth     :  %d", flxh->depth);
772       GST_INFO_OBJECT (flxdec, "speed     :  %d", flxh->speed);
773 
774       flxdec->next_time = 0;
775 
776       if (flxh->type == FLX_MAGICHDR_FLI) {
777         flxdec->frame_time = JIFFIE * flxh->speed;
778       } else if (flxh->speed == 0) {
779         flxdec->frame_time = GST_SECOND / 70;
780       } else {
781         flxdec->frame_time = flxh->speed * GST_MSECOND;
782       }
783 
784       flxdec->duration = flxh->frames * flxdec->frame_time;
785       GST_LOG ("duration   :  %" GST_TIME_FORMAT,
786           GST_TIME_ARGS (flxdec->duration));
787 
788       templ = gst_pad_get_pad_template_caps (flxdec->srcpad);
789       caps = gst_caps_copy (templ);
790       gst_caps_unref (templ);
791       gst_caps_set_simple (caps,
792           "width", G_TYPE_INT, flxh->width,
793           "height", G_TYPE_INT, flxh->height,
794           "framerate", GST_TYPE_FRACTION, (gint) GST_MSECOND,
795           (gint) flxdec->frame_time / 1000, NULL);
796 
797       gst_pad_set_caps (flxdec->srcpad, caps);
798       gst_caps_unref (caps);
799 
800       if (flxdec->need_segment) {
801         gst_pad_push_event (flxdec->srcpad,
802             gst_event_new_segment (&flxdec->segment));
803         flxdec->need_segment = FALSE;
804       }
805 
806       /* zero means 8 */
807       if (flxh->depth == 0)
808         flxh->depth = 8;
809 
810       if (flxh->depth != 8) {
811         GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE,
812             ("%s", "Don't know how to decode non 8 bit depth streams"), (NULL));
813         goto unmap_input_error;
814       }
815 
816       flxdec->converter =
817           flx_colorspace_converter_new (flxh->width, flxh->height);
818 
819       if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) {
820         GST_INFO_OBJECT (flxdec, "(FLC) aspect_dx :  %d", flxh->aspect_dx);
821         GST_INFO_OBJECT (flxdec, "(FLC) aspect_dy :  %d", flxh->aspect_dy);
822         GST_INFO_OBJECT (flxdec, "(FLC) oframe1   :  0x%08x", flxh->oframe1);
823         GST_INFO_OBJECT (flxdec, "(FLC) oframe2   :  0x%08x", flxh->oframe2);
824       }
825 
826       flxdec->size = ((guint) flxh->width * (guint) flxh->height);
827       if (flxdec->size >= G_MAXSIZE / 4) {
828         GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
829             ("%s", "Cannot allocate required memory"), (NULL));
830         goto unmap_input_error;
831       }
832 
833       /* create delta and output frame */
834       flxdec->frame_data = g_malloc0 (flxdec->size);
835       flxdec->delta_data = g_malloc0 (flxdec->size);
836 
837       flxdec->state = GST_FLXDEC_PLAYING;
838     }
839   } else if (flxdec->state == GST_FLXDEC_PLAYING) {
840     GstBuffer *out;
841 
842     /* while we have enough data in the adapter */
843     while (available >= FlxFrameChunkSize && res == GST_FLOW_OK) {
844       guint32 size;
845       guint16 type;
846 
847       if (!gst_byte_reader_get_uint32_le (&reader, &size))
848         goto parse_error;
849       if (available < size)
850         goto need_more_data;
851 
852       available -= size;
853       gst_adapter_flush (flxdec->adapter, size);
854 
855       if (!gst_byte_reader_get_uint16_le (&reader, &type))
856         goto parse_error;
857 
858       switch (type) {
859         case FLX_FRAME_TYPE:{
860           GstByteReader chunks;
861           GstByteWriter writer;
862           guint16 n_chunks;
863           GstMapInfo map;
864 
865           GST_LOG_OBJECT (flxdec, "Have frame type 0x%02x of size %d", type,
866               size);
867 
868           if (!gst_byte_reader_get_sub_reader (&reader, &chunks,
869                   size - FlxFrameChunkSize))
870             goto parse_error;
871 
872           if (!gst_byte_reader_get_uint16_le (&chunks, &n_chunks))
873             goto parse_error;
874           GST_LOG_OBJECT (flxdec, "Have %d chunks", n_chunks);
875 
876           if (n_chunks == 0)
877             break;
878           if (!gst_byte_reader_skip (&chunks, 8))       /* reserved */
879             goto parse_error;
880 
881           gst_byte_writer_init_with_data (&writer, flxdec->frame_data,
882               flxdec->size, TRUE);
883 
884           /* decode chunks */
885           if (!flx_decode_chunks (flxdec, n_chunks, &chunks, &writer)) {
886             GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
887                 ("%s", "Could not decode chunk"), NULL);
888             goto unmap_input_error;
889           }
890           gst_byte_writer_reset (&writer);
891 
892           /* save copy of the current frame for possible delta. */
893           memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size);
894 
895           out = gst_buffer_new_and_alloc (flxdec->size * 4);
896           if (!gst_buffer_map (out, &map, GST_MAP_WRITE)) {
897             GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
898                 ("%s", "Could not map output buffer"), NULL);
899             gst_buffer_unref (out);
900             goto unmap_input_error;
901           }
902 
903           /* convert current frame. */
904           flx_colorspace_convert (flxdec->converter, flxdec->frame_data,
905               map.data);
906           gst_buffer_unmap (out, &map);
907 
908           GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
909           flxdec->next_time += flxdec->frame_time;
910 
911           res = gst_pad_push (flxdec->srcpad, out);
912           break;
913         }
914         default:
915           GST_DEBUG_OBJECT (flxdec, "Unknown frame type 0x%02x, skipping %d",
916               type, size);
917           if (!gst_byte_reader_skip (&reader, size - FlxFrameChunkSize))
918             goto parse_error;
919           break;
920       }
921     }
922   }
923 
924 need_more_data:
925   gst_buffer_unmap (input, &map_info);
926   gst_buffer_unref (input);
927   return res;
928 
929   /* ERRORS */
930 parse_error:
931   GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
932       ("%s", "Failed to parse stream"), (NULL));
933 unmap_input_error:
934   gst_buffer_unmap (input, &map_info);
935 error:
936   gst_buffer_unref (input);
937   return GST_FLOW_ERROR;
938 }
939 
940 static GstStateChangeReturn
gst_flxdec_change_state(GstElement * element,GstStateChange transition)941 gst_flxdec_change_state (GstElement * element, GstStateChange transition)
942 {
943   GstFlxDec *flxdec;
944   GstStateChangeReturn ret;
945 
946   flxdec = GST_FLXDEC (element);
947 
948   switch (transition) {
949     case GST_STATE_CHANGE_NULL_TO_READY:
950       break;
951     case GST_STATE_CHANGE_READY_TO_PAUSED:
952       gst_adapter_clear (flxdec->adapter);
953       flxdec->state = GST_FLXDEC_READ_HEADER;
954       gst_segment_init (&flxdec->segment, GST_FORMAT_UNDEFINED);
955       flxdec->need_segment = TRUE;
956       break;
957     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
958       break;
959     default:
960       break;
961   }
962 
963   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
964 
965   switch (transition) {
966     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
967       break;
968     case GST_STATE_CHANGE_PAUSED_TO_READY:
969       if (flxdec->frame_data) {
970         g_free (flxdec->frame_data);
971         flxdec->frame_data = NULL;
972       }
973       if (flxdec->delta_data) {
974         g_free (flxdec->delta_data);
975         flxdec->delta_data = NULL;
976       }
977       if (flxdec->converter) {
978         flx_colorspace_converter_destroy (flxdec->converter);
979         flxdec->converter = NULL;
980       }
981       break;
982     case GST_STATE_CHANGE_READY_TO_NULL:
983       break;
984     default:
985       break;
986   }
987   return ret;
988 }
989 
990 static gboolean
plugin_init(GstPlugin * plugin)991 plugin_init (GstPlugin * plugin)
992 {
993   return GST_ELEMENT_REGISTER (flxdec, plugin);
994 }
995 
996 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
997     GST_VERSION_MINOR,
998     flxdec,
999     "FLC/FLI/FLX video decoder",
1000     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
1001