| From be670f0daf67304fb92c76aa09c30cae0bfd1fe4 Mon Sep 17 00:00:00 2001 |
| From: Matthew Waters <matthew@centricular.com> |
| Date: Wed, 23 Nov 2016 07:09:06 +1100 |
| Subject: [PATCH 4/4] flxdec: rewrite logic based on GstByteReader/Writer |
| |
| Solves overreading/writing the given arrays and will error out if the |
| streams asks to do that. |
| |
| Also does more error checking that the stream is valid and won't |
| overrun any allocated arrays. Also mitigate integer overflow errors |
| calculating allocation sizes. |
| |
| https://bugzilla.gnome.org/show_bug.cgi?id=774859 |
| --- |
| gst/flx/flx_color.c | 1 - |
| gst/flx/flx_fmt.h | 72 ------- |
| gst/flx/gstflxdec.c | 610 ++++++++++++++++++++++++++++++++++++---------------- |
| gst/flx/gstflxdec.h | 4 +- |
| 4 files changed, 427 insertions(+), 260 deletions(-) |
| |
| diff --git a/gst/flx/flx_color.c b/gst/flx/flx_color.c |
| index 047bfdf..3a58135 100644 |
| --- a/gst/flx/flx_color.c |
| +++ b/gst/flx/flx_color.c |
| @@ -101,7 +101,6 @@ flx_set_palette_vector (FlxColorSpaceConverter * flxpal, guint start, guint num, |
| } else { |
| memcpy (&flxpal->palvec[start * 3], newpal, grab * 3); |
| } |
| - |
| } |
| |
| void |
| diff --git a/gst/flx/flx_fmt.h b/gst/flx/flx_fmt.h |
| index 9ab31ba..abff200 100644 |
| --- a/gst/flx/flx_fmt.h |
| +++ b/gst/flx/flx_fmt.h |
| @@ -123,78 +123,6 @@ typedef struct _FlxFrameType |
| } FlxFrameType; |
| #define FlxFrameTypeSize 10 |
| |
| -#if G_BYTE_ORDER == G_BIG_ENDIAN |
| -#define LE_TO_BE_16(i16) ((guint16) (((i16) << 8) | ((i16) >> 8))) |
| -#define LE_TO_BE_32(i32) \ |
| - (((guint32) (LE_TO_BE_16((guint16) (i32))) << 16) | (LE_TO_BE_16((i32) >> 16))) |
| - |
| -#define FLX_FRAME_TYPE_FIX_ENDIANNESS(frm_type_p) \ |
| - do { \ |
| - (frm_type_p)->chunks = LE_TO_BE_16((frm_type_p)->chunks); \ |
| - (frm_type_p)->delay = LE_TO_BE_16((frm_type_p)->delay); \ |
| - } while(0) |
| - |
| -#define FLX_HUFFMAN_TABLE_FIX_ENDIANNESS(hffmn_table_p) \ |
| - do { \ |
| - (hffmn_table_p)->codelength = \ |
| - LE_TO_BE_16((hffmn_table_p)->codelength); \ |
| - (hffmn_table_p)->numcodes = LE_TO_BE_16((hffmn_table_p)->numcodes); \ |
| - } while(0) |
| - |
| -#define FLX_SEGMENT_TABLE_FIX_ENDIANNESS(sgmnt_table_p) \ |
| - ((sgmnt_table_p)->segments = LE_TO_BE_16((sgmnt_table_p)->segments)) |
| - |
| -#define FLX_PREFIX_CHUNK_FIX_ENDIANNESS(prfx_chnk_p) \ |
| - do { \ |
| - (prfx_chnk_p)->chunks = LE_TO_BE_16((prfx_chnk_p)->chunks); \ |
| - } while(0) |
| - |
| -#define FLX_FRAME_CHUNK_FIX_ENDIANNESS(frm_chnk_p) \ |
| - do { \ |
| - (frm_chnk_p)->size = LE_TO_BE_32((frm_chnk_p)->size); \ |
| - (frm_chnk_p)->id = LE_TO_BE_16((frm_chnk_p)->id); \ |
| - } while(0) |
| - |
| -#define FLX_HDR_FIX_ENDIANNESS(hdr_p) \ |
| - do { \ |
| - (hdr_p)->size = LE_TO_BE_32((hdr_p)->size); \ |
| - (hdr_p)->type = LE_TO_BE_16((hdr_p)->type); \ |
| - (hdr_p)->frames = LE_TO_BE_16((hdr_p)->frames); \ |
| - (hdr_p)->width = LE_TO_BE_16((hdr_p)->width); \ |
| - (hdr_p)->height = LE_TO_BE_16((hdr_p)->height); \ |
| - (hdr_p)->depth = LE_TO_BE_16((hdr_p)->depth); \ |
| - (hdr_p)->flags = LE_TO_BE_16((hdr_p)->flags); \ |
| - (hdr_p)->speed = LE_TO_BE_32((hdr_p)->speed); \ |
| - (hdr_p)->reserved1 = LE_TO_BE_16((hdr_p)->reserved1); \ |
| - (hdr_p)->created = LE_TO_BE_32((hdr_p)->created); \ |
| - (hdr_p)->creator = LE_TO_BE_32((hdr_p)->creator); \ |
| - (hdr_p)->updated = LE_TO_BE_32((hdr_p)->updated); \ |
| - (hdr_p)->updater = LE_TO_BE_32((hdr_p)->updater); \ |
| - (hdr_p)->aspect_dx = LE_TO_BE_16((hdr_p)->aspect_dx); \ |
| - (hdr_p)->aspect_dy = LE_TO_BE_16((hdr_p)->aspect_dy); \ |
| - (hdr_p)->ext_flags = LE_TO_BE_16((hdr_p)->ext_flags); \ |
| - (hdr_p)->keyframes = LE_TO_BE_16((hdr_p)->keyframes); \ |
| - (hdr_p)->totalframes = LE_TO_BE_16((hdr_p)->totalframes); \ |
| - (hdr_p)->req_memory = LE_TO_BE_32((hdr_p)->req_memory); \ |
| - (hdr_p)->max_regions = LE_TO_BE_16((hdr_p)->max_regions); \ |
| - (hdr_p)->transp_num = LE_TO_BE_16((hdr_p)->transp_num); \ |
| - (hdr_p)->oframe1 = LE_TO_BE_32((hdr_p)->oframe1); \ |
| - (hdr_p)->oframe2 = LE_TO_BE_32((hdr_p)->oframe2); \ |
| - } while(0) |
| -#else |
| - |
| -#define LE_TO_BE_16(i16) ((i16)) |
| -#define LE_TO_BE_32(i32) ((i32)) |
| - |
| -#define FLX_FRAME_TYPE_FIX_ENDIANNESS(frm_type_p) |
| -#define FLX_HUFFMAN_TABLE_FIX_ENDIANNESS(hffmn_table_p) |
| -#define FLX_SEGMENT_TABLE_FIX_ENDIANNESS(sgmnt_table_p) |
| -#define FLX_PREFIX_CHUNK_FIX_ENDIANNESS(prfx_chnk_p) |
| -#define FLX_FRAME_CHUNK_FIX_ENDIANNESS(frm_chnk_p) |
| -#define FLX_HDR_FIX_ENDIANNESS(hdr_p) |
| - |
| -#endif /* G_BYTE_ORDER == G_BIG_ENDIAN */ |
| - |
| G_END_DECLS |
| |
| #endif /* __GST_FLX_FMT_H__ */ |
| diff --git a/gst/flx/gstflxdec.c b/gst/flx/gstflxdec.c |
| index a237976..aa1bed5 100644 |
| --- a/gst/flx/gstflxdec.c |
| +++ b/gst/flx/gstflxdec.c |
| @@ -1,5 +1,6 @@ |
| /* GStreamer |
| * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com> |
| + * Copyright (C) <2016> Matthew Waters <matthew@centricular.com> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| @@ -24,6 +25,7 @@ |
| /* |
| * http://www.coolutils.com/Formats/FLI |
| * http://woodshole.er.usgs.gov/operations/modeling/flc.html |
| + * http://www.compuphase.com/flic.htm |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| @@ -73,10 +75,14 @@ static GstStateChangeReturn gst_flxdec_change_state (GstElement * element, |
| static gboolean gst_flxdec_src_query_handler (GstPad * pad, GstObject * parent, |
| GstQuery * query); |
| |
| -static void flx_decode_color (GstFlxDec *, guchar *, guchar *, gint); |
| -static gboolean flx_decode_brun (GstFlxDec *, guchar *, guchar *); |
| -static gboolean flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *); |
| -static gboolean flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *); |
| +static gboolean flx_decode_color (GstFlxDec * flxdec, GstByteReader * reader, |
| + GstByteWriter * writer, gint scale); |
| +static gboolean flx_decode_brun (GstFlxDec * flxdec, |
| + GstByteReader * reader, GstByteWriter * writer); |
| +static gboolean flx_decode_delta_fli (GstFlxDec * flxdec, |
| + GstByteReader * reader, GstByteWriter * writer); |
| +static gboolean flx_decode_delta_flc (GstFlxDec * flxdec, |
| + GstByteReader * reader, GstByteWriter * writer); |
| |
| #define rndalign(off) ((off) + ((off) & 1)) |
| |
| @@ -204,57 +210,59 @@ gst_flxdec_sink_event_handler (GstPad * pad, GstObject * parent, |
| } |
| |
| static gboolean |
| -flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data, |
| - guchar * dest) |
| +flx_decode_chunks (GstFlxDec * flxdec, gulong n_chunks, GstByteReader * reader, |
| + GstByteWriter * writer) |
| { |
| - FlxFrameChunk *hdr; |
| gboolean ret = TRUE; |
| |
| - g_return_val_if_fail (data != NULL, FALSE); |
| - |
| - while (count--) { |
| - hdr = (FlxFrameChunk *) data; |
| - FLX_FRAME_CHUNK_FIX_ENDIANNESS (hdr); |
| - data += FlxFrameChunkSize; |
| + while (n_chunks--) { |
| + GstByteReader chunk; |
| + guint32 size; |
| + guint16 type; |
| + |
| + if (!gst_byte_reader_get_uint32_le (reader, &size)) |
| + goto parse_error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &type)) |
| + goto parse_error; |
| + GST_LOG_OBJECT (flxdec, "chunk has type 0x%02x size %d", type, size); |
| + |
| + if (!gst_byte_reader_get_sub_reader (reader, &chunk, |
| + size - FlxFrameChunkSize)) { |
| + GST_ERROR_OBJECT (flxdec, "Incorrect size in the chunk header"); |
| + goto error; |
| + } |
| |
| - switch (hdr->id) { |
| + switch (type) { |
| case FLX_COLOR64: |
| - flx_decode_color (flxdec, data, dest, 2); |
| - data += rndalign (hdr->size) - FlxFrameChunkSize; |
| + ret = flx_decode_color (flxdec, &chunk, writer, 2); |
| break; |
| |
| case FLX_COLOR256: |
| - flx_decode_color (flxdec, data, dest, 0); |
| - data += rndalign (hdr->size) - FlxFrameChunkSize; |
| + ret = flx_decode_color (flxdec, &chunk, writer, 0); |
| break; |
| |
| case FLX_BRUN: |
| - ret = flx_decode_brun (flxdec, data, dest); |
| - data += rndalign (hdr->size) - FlxFrameChunkSize; |
| + ret = flx_decode_brun (flxdec, &chunk, writer); |
| break; |
| |
| case FLX_LC: |
| - ret = flx_decode_delta_fli (flxdec, data, dest); |
| - data += rndalign (hdr->size) - FlxFrameChunkSize; |
| + ret = flx_decode_delta_fli (flxdec, &chunk, writer); |
| break; |
| |
| case FLX_SS2: |
| - ret = flx_decode_delta_flc (flxdec, data, dest); |
| - data += rndalign (hdr->size) - FlxFrameChunkSize; |
| + ret = flx_decode_delta_flc (flxdec, &chunk, writer); |
| break; |
| |
| case FLX_BLACK: |
| - memset (dest, 0, flxdec->size); |
| + ret = gst_byte_writer_fill (writer, 0, flxdec->size); |
| break; |
| |
| case FLX_MINI: |
| - data += rndalign (hdr->size) - FlxFrameChunkSize; |
| break; |
| |
| default: |
| - GST_WARNING ("Unimplented chunk type: 0x%02x size: %d - skipping", |
| - hdr->id, hdr->size); |
| - data += rndalign (hdr->size) - FlxFrameChunkSize; |
| + GST_WARNING ("Unimplemented chunk type: 0x%02x size: %d - skipping", |
| + type, size); |
| break; |
| } |
| |
| @@ -263,43 +271,60 @@ flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data, |
| } |
| |
| return ret; |
| + |
| +parse_error: |
| + GST_ERROR_OBJECT (flxdec, "Failed to decode chunk"); |
| +error: |
| + return FALSE; |
| } |
| |
| |
| -static void |
| -flx_decode_color (GstFlxDec * flxdec, guchar * data, guchar * dest, gint scale) |
| +static gboolean |
| +flx_decode_color (GstFlxDec * flxdec, GstByteReader * reader, |
| + GstByteWriter * writer, gint scale) |
| { |
| - guint packs, count, indx; |
| + guint8 count, indx; |
| + guint16 packs; |
| |
| - g_return_if_fail (flxdec != NULL); |
| - |
| - packs = (data[0] + (data[1] << 8)); |
| - |
| - data += 2; |
| + if (!gst_byte_reader_get_uint16_le (reader, &packs)) |
| + goto error; |
| indx = 0; |
| |
| - GST_LOG ("GstFlxDec: cmap packs: %d", packs); |
| + GST_LOG ("GstFlxDec: cmap packs: %d", (guint) packs); |
| while (packs--) { |
| + const guint8 *data; |
| + guint16 actual_count; |
| + |
| /* color map index + skip count */ |
| - indx += *data++; |
| + if (!gst_byte_reader_get_uint8 (reader, &indx)) |
| + goto error; |
| |
| /* number of rgb triplets */ |
| - count = *data++ & 0xff; |
| - if (count == 0) |
| - count = 256; |
| + if (!gst_byte_reader_get_uint8 (reader, &count)) |
| + goto error; |
| |
| - GST_LOG ("GstFlxDec: cmap count: %d (indx: %d)", count, indx); |
| - flx_set_palette_vector (flxdec->converter, indx, count, data, scale); |
| + actual_count = count == 0 ? 256 : count; |
| |
| - data += (count * 3); |
| + if (!gst_byte_reader_get_data (reader, count * 3, &data)) |
| + goto error; |
| + |
| + GST_LOG_OBJECT (flxdec, "cmap count: %d (indx: %d)", actual_count, indx); |
| + flx_set_palette_vector (flxdec->converter, indx, actual_count, |
| + (guchar *) data, scale); |
| } |
| + |
| + return TRUE; |
| + |
| +error: |
| + GST_ERROR_OBJECT (flxdec, "Error decoding color palette"); |
| + return FALSE; |
| } |
| |
| static gboolean |
| -flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| +flx_decode_brun (GstFlxDec * flxdec, GstByteReader * reader, |
| + GstByteWriter * writer) |
| { |
| - gulong count, lines, row; |
| - guchar x; |
| + gulong lines, row; |
| |
| g_return_val_if_fail (flxdec != NULL, FALSE); |
| |
| @@ -310,82 +335,125 @@ flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| * contain more then 255 RLE packets. we use the frame |
| * width instead. |
| */ |
| - data++; |
| + if (!gst_byte_reader_skip (reader, 1)) |
| + goto error; |
| |
| row = flxdec->hdr.width; |
| while (row) { |
| - count = *data++; |
| + gint8 count; |
| + |
| + if (!gst_byte_reader_get_int8 (reader, &count)) |
| + goto error; |
| + |
| + if (count <= 0) { |
| + const guint8 *data; |
| |
| - if (count > 0x7f) { |
| /* literal run */ |
| - count = 0x100 - count; |
| - if ((glong) row - (glong) count < 0) { |
| - GST_ERROR_OBJECT (flxdec, "Invalid BRUN packet detected."); |
| + count = ABS (count); |
| + |
| + GST_LOG_OBJECT (flxdec, "have literal run of size %d", count); |
| + |
| + if (count > row) { |
| + GST_ERROR_OBJECT (flxdec, "Invalid BRUN line detected. " |
| + "bytes to write exceeds the end of the row"); |
| return FALSE; |
| } |
| row -= count; |
| |
| - while (count--) |
| - *dest++ = *data++; |
| - |
| + if (!gst_byte_reader_get_data (reader, count, &data)) |
| + goto error; |
| + if (!gst_byte_writer_put_data (writer, data, count)) |
| + goto error; |
| } else { |
| - if ((glong) row - (glong) count < 0) { |
| - GST_ERROR_OBJECT (flxdec, "Invalid BRUN packet detected."); |
| + guint8 x; |
| + |
| + GST_LOG_OBJECT (flxdec, "have replicate run of size %d", count); |
| + |
| + if (count > row) { |
| + GST_ERROR_OBJECT (flxdec, "Invalid BRUN packet detected." |
| + "bytes to write exceeds the end of the row"); |
| return FALSE; |
| } |
| |
| /* replicate run */ |
| row -= count; |
| - x = *data++; |
| |
| - while (count--) |
| - *dest++ = x; |
| + if (!gst_byte_reader_get_uint8 (reader, &x)) |
| + goto error; |
| + if (!gst_byte_writer_fill (writer, x, count)) |
| + goto error; |
| } |
| } |
| } |
| |
| return TRUE; |
| + |
| +error: |
| + GST_ERROR_OBJECT (flxdec, "Failed to decode BRUN packet"); |
| + return FALSE; |
| } |
| |
| static gboolean |
| -flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| +flx_decode_delta_fli (GstFlxDec * flxdec, GstByteReader * reader, |
| + GstByteWriter * writer) |
| { |
| - gulong count, packets, lines, start_line; |
| - guchar *start_p, x; |
| + guint16 start_line, lines; |
| + guint line_start_i; |
| |
| g_return_val_if_fail (flxdec != NULL, FALSE); |
| g_return_val_if_fail (flxdec->delta_data != NULL, FALSE); |
| |
| /* use last frame for delta */ |
| - memcpy (dest, flxdec->delta_data, flxdec->size); |
| + if (!gst_byte_writer_put_data (writer, flxdec->delta_data, flxdec->size)) |
| + goto error; |
| + |
| + if (!gst_byte_reader_get_uint16_le (reader, &start_line)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &lines)) |
| + goto error; |
| + GST_LOG_OBJECT (flxdec, "height %d start line %d line count %d", |
| + flxdec->hdr.height, start_line, lines); |
| |
| - start_line = (data[0] + (data[1] << 8)); |
| - lines = (data[2] + (data[3] << 8)); |
| if (start_line + lines > flxdec->hdr.height) { |
| GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. too many lines."); |
| return FALSE; |
| } |
| - data += 4; |
| |
| - /* start position of delta */ |
| - dest += (flxdec->hdr.width * start_line); |
| - start_p = dest; |
| + line_start_i = flxdec->hdr.width * start_line; |
| + if (!gst_byte_writer_set_pos (writer, line_start_i)) |
| + goto error; |
| |
| while (lines--) { |
| + guint8 packets; |
| + |
| /* packet count */ |
| - packets = *data++; |
| + if (!gst_byte_reader_get_uint8 (reader, &packets)) |
| + goto error; |
| + GST_LOG_OBJECT (flxdec, "have %d packets", packets); |
| |
| while (packets--) { |
| /* skip count */ |
| - guchar skip = *data++; |
| - dest += skip; |
| + guint8 skip; |
| + gint8 count; |
| + if (!gst_byte_reader_get_uint8 (reader, &skip)) |
| + goto error; |
| + |
| + /* skip bytes */ |
| + if (!gst_byte_writer_set_pos (writer, |
| + gst_byte_writer_get_pos (writer) + skip)) |
| + goto error; |
| |
| /* RLE count */ |
| - count = *data++; |
| + if (!gst_byte_reader_get_int8 (reader, &count)) |
| + goto error; |
| + |
| + if (count < 0) { |
| + guint8 x; |
| |
| - if (count > 0x7f) { |
| /* literal run */ |
| - count = 0x100 - count; |
| + count = ABS (count); |
| + GST_LOG_OBJECT (flxdec, "have literal run of size %d at offset %d", |
| + count, skip); |
| |
| if (skip + count > flxdec->hdr.width) { |
| GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. " |
| @@ -393,11 +461,16 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| return FALSE; |
| } |
| |
| - x = *data++; |
| - while (count--) |
| - *dest++ = x; |
| - |
| + if (!gst_byte_reader_get_uint8 (reader, &x)) |
| + goto error; |
| + if (!gst_byte_writer_fill (writer, x, count)) |
| + goto error; |
| } else { |
| + const guint8 *data; |
| + |
| + GST_LOG_OBJECT (flxdec, "have replicate run of size %d at offset %d", |
| + count, skip); |
| + |
| if (skip + count > flxdec->hdr.width) { |
| GST_ERROR_OBJECT (flxdec, "Invalid FLI packet detected. " |
| "line too long."); |
| @@ -405,45 +478,60 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| } |
| |
| /* replicate run */ |
| - while (count--) |
| - *dest++ = *data++; |
| + if (!gst_byte_reader_get_data (reader, count, &data)) |
| + goto error; |
| + if (!gst_byte_writer_put_data (writer, data, count)) |
| + goto error; |
| } |
| } |
| - start_p += flxdec->hdr.width; |
| - dest = start_p; |
| + line_start_i += flxdec->hdr.width; |
| + if (!gst_byte_writer_set_pos (writer, line_start_i)) |
| + goto error; |
| } |
| |
| return TRUE; |
| + |
| +error: |
| + GST_ERROR_OBJECT (flxdec, "Failed to decode FLI packet"); |
| + return FALSE; |
| } |
| |
| static gboolean |
| -flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| +flx_decode_delta_flc (GstFlxDec * flxdec, GstByteReader * reader, |
| + GstByteWriter * writer) |
| { |
| - gulong count, lines, start_l, opcode; |
| - guchar *start_p; |
| + guint16 lines, start_l; |
| |
| g_return_val_if_fail (flxdec != NULL, FALSE); |
| g_return_val_if_fail (flxdec->delta_data != NULL, FALSE); |
| |
| /* use last frame for delta */ |
| - memcpy (dest, flxdec->delta_data, flxdec->size); |
| + if (!gst_byte_writer_put_data (writer, flxdec->delta_data, flxdec->size)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &lines)) |
| + goto error; |
| |
| - lines = (data[0] + (data[1] << 8)); |
| if (lines > flxdec->hdr.height) { |
| GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. too many lines."); |
| return FALSE; |
| } |
| - data += 2; |
| |
| - start_p = dest; |
| start_l = lines; |
| |
| while (lines) { |
| - dest = start_p + (flxdec->hdr.width * (start_l - lines)); |
| + guint16 opcode; |
| + |
| + if (!gst_byte_writer_set_pos (writer, |
| + flxdec->hdr.width * (start_l - lines))) |
| + goto error; |
| |
| /* process opcode(s) */ |
| - while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) { |
| - data += 2; |
| + while (TRUE) { |
| + if (!gst_byte_reader_get_uint16_le (reader, &opcode)) |
| + goto error; |
| + if ((opcode & 0xc000) == 0) |
| + break; |
| + |
| if ((opcode & 0xc000) == 0xc000) { |
| /* line skip count */ |
| gulong skip = (0x10000 - opcode); |
| @@ -453,27 +541,44 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| return FALSE; |
| } |
| start_l += skip; |
| - dest += flxdec->hdr.width * skip; |
| + if (!gst_byte_writer_set_pos (writer, |
| + gst_byte_writer_get_pos (writer) + flxdec->hdr.width * skip)) |
| + goto error; |
| } else { |
| /* last pixel */ |
| - dest += flxdec->hdr.width; |
| - *dest++ = (opcode & 0xff); |
| + if (!gst_byte_writer_set_pos (writer, |
| + gst_byte_writer_get_pos (writer) + flxdec->hdr.width)) |
| + goto error; |
| + if (!gst_byte_writer_put_uint8 (writer, opcode & 0xff)) |
| + goto error; |
| } |
| } |
| - data += 2; |
| |
| /* last opcode is the packet count */ |
| + GST_LOG_OBJECT (flxdec, "have %d packets", opcode); |
| while (opcode--) { |
| /* skip count */ |
| - guchar skip = *data++; |
| - dest += skip; |
| + guint8 skip; |
| + gint8 count; |
| + |
| + if (!gst_byte_reader_get_uint8 (reader, &skip)) |
| + goto error; |
| + if (!gst_byte_writer_set_pos (writer, |
| + gst_byte_writer_get_pos (writer) + skip)) |
| + goto error; |
| |
| /* RLE count */ |
| - count = *data++; |
| + if (!gst_byte_reader_get_int8 (reader, &count)) |
| + goto error; |
| + |
| + if (count < 0) { |
| + guint16 x; |
| |
| - if (count > 0x7f) { |
| /* replicate word run */ |
| - count = 0x100 - count; |
| + count = ABS (count); |
| + |
| + GST_LOG_OBJECT (flxdec, "have replicate run of size %d at offset %d", |
| + count, skip); |
| |
| if (skip + count > flxdec->hdr.width) { |
| GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. " |
| @@ -481,22 +586,31 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| return FALSE; |
| } |
| |
| + if (!gst_byte_reader_get_uint16_le (reader, &x)) |
| + goto error; |
| + |
| while (count--) { |
| - *dest++ = data[0]; |
| - *dest++ = data[1]; |
| + if (!gst_byte_writer_put_uint16_le (writer, x)) { |
| + goto error; |
| + } |
| } |
| - data += 2; |
| } else { |
| + GST_LOG_OBJECT (flxdec, "have literal run of size %d at offset %d", |
| + count, skip); |
| + |
| if (skip + count > flxdec->hdr.width) { |
| GST_ERROR_OBJECT (flxdec, "Invalid FLC packet detected. " |
| "line too long."); |
| return FALSE; |
| } |
| |
| - /* literal word run */ |
| while (count--) { |
| - *dest++ = *data++; |
| - *dest++ = *data++; |
| + guint16 x; |
| + |
| + if (!gst_byte_reader_get_uint16_le (reader, &x)) |
| + goto error; |
| + if (!gst_byte_writer_put_uint16_le (writer, x)) |
| + goto error; |
| } |
| } |
| } |
| @@ -504,13 +618,91 @@ flx_decode_delta_flc (GstFlxDec * flxdec, guchar * data, guchar * dest) |
| } |
| |
| return TRUE; |
| + |
| +error: |
| + GST_ERROR_OBJECT (flxdec, "Failed to decode FLI packet"); |
| + return FALSE; |
| +} |
| + |
| +static gboolean |
| +_read_flx_header (GstFlxDec * flxdec, GstByteReader * reader, FlxHeader * flxh) |
| +{ |
| + memset (flxh, 0, sizeof (*flxh)); |
| + |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->size)) |
| + goto error; |
| + if (flxh->size < FlxHeaderSize) { |
| + GST_ERROR_OBJECT (flxdec, "Invalid file size in the header"); |
| + return FALSE; |
| + } |
| + |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->type)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->frames)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->width)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->height)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->depth)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->flags)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->speed)) |
| + goto error; |
| + if (!gst_byte_reader_skip (reader, 2)) /* reserved */ |
| + goto error; |
| + /* FLC */ |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->created)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->creator)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->updated)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->updater)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->aspect_dx)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->aspect_dy)) |
| + goto error; |
| + /* EGI */ |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->ext_flags)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->keyframes)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->totalframes)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->req_memory)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->max_regions)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint16_le (reader, &flxh->transp_num)) |
| + goto error; |
| + if (!gst_byte_reader_skip (reader, 24)) /* reserved */ |
| + goto error; |
| + /* FLC */ |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->oframe1)) |
| + goto error; |
| + if (!gst_byte_reader_get_uint32_le (reader, &flxh->oframe2)) |
| + goto error; |
| + if (!gst_byte_reader_skip (reader, 40)) /* reserved */ |
| + goto error; |
| + |
| + return TRUE; |
| + |
| +error: |
| + GST_ERROR_OBJECT (flxdec, "Error reading file header"); |
| + return FALSE; |
| } |
| |
| static GstFlowReturn |
| gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| { |
| + GstByteReader reader; |
| + GstBuffer *input; |
| + GstMapInfo map_info; |
| GstCaps *caps; |
| - guint avail; |
| + guint available; |
| GstFlowReturn res = GST_FLOW_OK; |
| |
| GstFlxDec *flxdec; |
| @@ -521,31 +713,50 @@ gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| g_return_val_if_fail (flxdec != NULL, GST_FLOW_ERROR); |
| |
| gst_adapter_push (flxdec->adapter, buf); |
| - avail = gst_adapter_available (flxdec->adapter); |
| + available = gst_adapter_available (flxdec->adapter); |
| + input = gst_adapter_get_buffer (flxdec->adapter, available); |
| + if (!gst_buffer_map (input, &map_info, GST_MAP_READ)) { |
| + GST_ELEMENT_ERROR (flxdec, STREAM, DECODE, |
| + ("%s", "Failed to map buffer"), (NULL)); |
| + goto error; |
| + } |
| + gst_byte_reader_init (&reader, map_info.data, map_info.size); |
| |
| if (flxdec->state == GST_FLXDEC_READ_HEADER) { |
| - if (avail >= FlxHeaderSize) { |
| - const guint8 *data = gst_adapter_map (flxdec->adapter, FlxHeaderSize); |
| + if (available >= FlxHeaderSize) { |
| + GstByteReader header; |
| GstCaps *templ; |
| |
| - memcpy ((gchar *) & flxdec->hdr, data, FlxHeaderSize); |
| - FLX_HDR_FIX_ENDIANNESS (&(flxdec->hdr)); |
| - gst_adapter_unmap (flxdec->adapter); |
| + if (!gst_byte_reader_get_sub_reader (&reader, &header, FlxHeaderSize)) { |
| + GST_ELEMENT_ERROR (flxdec, STREAM, DECODE, |
| + ("%s", "Could not read header"), (NULL)); |
| + goto unmap_input_error; |
| + } |
| gst_adapter_flush (flxdec->adapter, FlxHeaderSize); |
| + available -= FlxHeaderSize; |
| + |
| + if (!_read_flx_header (flxdec, &header, &flxdec->hdr)) { |
| + GST_ELEMENT_ERROR (flxdec, STREAM, DECODE, |
| + ("%s", "Failed to parse header"), (NULL)); |
| + goto unmap_input_error; |
| + } |
| |
| flxh = &flxdec->hdr; |
| |
| /* check header */ |
| if (flxh->type != FLX_MAGICHDR_FLI && |
| - flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX) |
| - goto wrong_type; |
| + flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX) { |
| + GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL), |
| + ("not a flx file (type %x)", flxh->type)); |
| + goto unmap_input_error; |
| + } |
| |
| - GST_LOG ("size : %d", flxh->size); |
| - GST_LOG ("frames : %d", flxh->frames); |
| - GST_LOG ("width : %d", flxh->width); |
| - GST_LOG ("height : %d", flxh->height); |
| - GST_LOG ("depth : %d", flxh->depth); |
| - GST_LOG ("speed : %d", flxh->speed); |
| + GST_INFO_OBJECT (flxdec, "size : %d", flxh->size); |
| + GST_INFO_OBJECT (flxdec, "frames : %d", flxh->frames); |
| + GST_INFO_OBJECT (flxdec, "width : %d", flxh->width); |
| + GST_INFO_OBJECT (flxdec, "height : %d", flxh->height); |
| + GST_INFO_OBJECT (flxdec, "depth : %d", flxh->depth); |
| + GST_INFO_OBJECT (flxdec, "speed : %d", flxh->speed); |
| |
| flxdec->next_time = 0; |
| |
| @@ -573,18 +784,32 @@ gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| gst_pad_set_caps (flxdec->srcpad, caps); |
| gst_caps_unref (caps); |
| |
| - if (flxh->depth <= 8) |
| - flxdec->converter = |
| - flx_colorspace_converter_new (flxh->width, flxh->height); |
| + /* zero means 8 */ |
| + if (flxh->depth == 0) |
| + flxh->depth = 8; |
| + |
| + if (flxh->depth != 8) { |
| + GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, |
| + ("%s", "Don't know how to decode non 8 bit depth streams"), (NULL)); |
| + goto unmap_input_error; |
| + } |
| + |
| + flxdec->converter = |
| + flx_colorspace_converter_new (flxh->width, flxh->height); |
| |
| if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) { |
| - GST_LOG ("(FLC) aspect_dx : %d", flxh->aspect_dx); |
| - GST_LOG ("(FLC) aspect_dy : %d", flxh->aspect_dy); |
| - GST_LOG ("(FLC) oframe1 : 0x%08x", flxh->oframe1); |
| - GST_LOG ("(FLC) oframe2 : 0x%08x", flxh->oframe2); |
| + GST_INFO_OBJECT (flxdec, "(FLC) aspect_dx : %d", flxh->aspect_dx); |
| + GST_INFO_OBJECT (flxdec, "(FLC) aspect_dy : %d", flxh->aspect_dy); |
| + GST_INFO_OBJECT (flxdec, "(FLC) oframe1 : 0x%08x", flxh->oframe1); |
| + GST_INFO_OBJECT (flxdec, "(FLC) oframe2 : 0x%08x", flxh->oframe2); |
| } |
| |
| flxdec->size = ((guint) flxh->width * (guint) flxh->height); |
| + if (flxdec->size >= G_MAXSIZE / 4) { |
| + GST_ELEMENT_ERROR (flxdec, STREAM, DECODE, |
| + ("%s", "Cannot allocate required memory"), (NULL)); |
| + goto unmap_input_error; |
| + } |
| |
| /* create delta and output frame */ |
| flxdec->frame_data = g_malloc (flxdec->size); |
| @@ -596,55 +821,66 @@ gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| GstBuffer *out; |
| |
| /* while we have enough data in the adapter */ |
| - while (avail >= FlxFrameChunkSize && res == GST_FLOW_OK) { |
| - FlxFrameChunk flxfh; |
| - guchar *chunk; |
| - const guint8 *data; |
| - GstMapInfo map; |
| - |
| - chunk = NULL; |
| - data = gst_adapter_map (flxdec->adapter, FlxFrameChunkSize); |
| - memcpy (&flxfh, data, FlxFrameChunkSize); |
| - FLX_FRAME_CHUNK_FIX_ENDIANNESS (&flxfh); |
| - gst_adapter_unmap (flxdec->adapter); |
| - |
| - switch (flxfh.id) { |
| - case FLX_FRAME_TYPE: |
| - /* check if we have the complete frame */ |
| - if (avail < flxfh.size) |
| - goto need_more_data; |
| - |
| - /* flush header */ |
| - gst_adapter_flush (flxdec->adapter, FlxFrameChunkSize); |
| - |
| - chunk = gst_adapter_take (flxdec->adapter, |
| - flxfh.size - FlxFrameChunkSize); |
| - FLX_FRAME_TYPE_FIX_ENDIANNESS ((FlxFrameType *) chunk); |
| - if (((FlxFrameType *) chunk)->chunks == 0) |
| - break; |
| + while (available >= FlxFrameChunkSize && res == GST_FLOW_OK) { |
| + guint32 size; |
| + guint16 type; |
| |
| - /* create 32 bits output frame */ |
| -// res = gst_pad_alloc_buffer_and_set_caps (flxdec->srcpad, |
| -// GST_BUFFER_OFFSET_NONE, |
| -// flxdec->size * 4, GST_PAD_CAPS (flxdec->srcpad), &out); |
| -// if (res != GST_FLOW_OK) |
| -// break; |
| + if (!gst_byte_reader_get_uint32_le (&reader, &size)) |
| + goto parse_error; |
| + if (available < size) |
| + goto need_more_data; |
| |
| - out = gst_buffer_new_and_alloc (flxdec->size * 4); |
| + available -= size; |
| + gst_adapter_flush (flxdec->adapter, size); |
| + |
| + if (!gst_byte_reader_get_uint16_le (&reader, &type)) |
| + goto parse_error; |
| + |
| + switch (type) { |
| + case FLX_FRAME_TYPE:{ |
| + GstByteReader chunks; |
| + GstByteWriter writer; |
| + guint16 n_chunks; |
| + GstMapInfo map; |
| + |
| + GST_LOG_OBJECT (flxdec, "Have frame type 0x%02x of size %d", type, |
| + size); |
| + |
| + if (!gst_byte_reader_get_sub_reader (&reader, &chunks, |
| + size - FlxFrameChunkSize)) |
| + goto parse_error; |
| + |
| + if (!gst_byte_reader_get_uint16_le (&chunks, &n_chunks)) |
| + goto parse_error; |
| + GST_LOG_OBJECT (flxdec, "Have %d chunks", n_chunks); |
| + |
| + if (n_chunks == 0) |
| + break; |
| + if (!gst_byte_reader_skip (&chunks, 8)) /* reserved */ |
| + goto parse_error; |
| + |
| + gst_byte_writer_init_with_data (&writer, flxdec->frame_data, |
| + flxdec->size, TRUE); |
| |
| /* decode chunks */ |
| - if (!flx_decode_chunks (flxdec, |
| - ((FlxFrameType *) chunk)->chunks, |
| - chunk + FlxFrameTypeSize, flxdec->frame_data)) { |
| + if (!flx_decode_chunks (flxdec, n_chunks, &chunks, &writer)) { |
| GST_ELEMENT_ERROR (flxdec, STREAM, DECODE, |
| ("%s", "Could not decode chunk"), NULL); |
| - return GST_FLOW_ERROR; |
| + goto unmap_input_error; |
| } |
| + gst_byte_writer_reset (&writer); |
| |
| /* save copy of the current frame for possible delta. */ |
| memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size); |
| |
| - gst_buffer_map (out, &map, GST_MAP_WRITE); |
| + out = gst_buffer_new_and_alloc (flxdec->size * 4); |
| + if (!gst_buffer_map (out, &map, GST_MAP_WRITE)) { |
| + GST_ELEMENT_ERROR (flxdec, STREAM, DECODE, |
| + ("%s", "Could not map output buffer"), NULL); |
| + gst_buffer_unref (out); |
| + goto unmap_input_error; |
| + } |
| + |
| /* convert current frame. */ |
| flx_colorspace_convert (flxdec->converter, flxdec->frame_data, |
| map.data); |
| @@ -655,30 +891,32 @@ gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| |
| res = gst_pad_push (flxdec->srcpad, out); |
| break; |
| + } |
| default: |
| - /* check if we have the complete frame */ |
| - if (avail < flxfh.size) |
| - goto need_more_data; |
| - |
| - gst_adapter_flush (flxdec->adapter, flxfh.size); |
| + GST_DEBUG_OBJECT (flxdec, "Unknown frame type 0x%02x, skipping %d", |
| + type, size); |
| + if (!gst_byte_reader_skip (&reader, size - FlxFrameChunkSize)) |
| + goto parse_error; |
| break; |
| } |
| - |
| - g_free (chunk); |
| - |
| - avail = gst_adapter_available (flxdec->adapter); |
| } |
| } |
| + |
| + gst_buffer_unmap (input, &map_info); |
| + gst_buffer_unref (input); |
| + |
| need_more_data: |
| return res; |
| |
| /* ERRORS */ |
| -wrong_type: |
| - { |
| - GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL), |
| - ("not a flx file (type %x)", flxh->type)); |
| - return GST_FLOW_ERROR; |
| - } |
| +parse_error: |
| + GST_ELEMENT_ERROR (flxdec, STREAM, DECODE, |
| + ("%s", "Failed to parse stream"), (NULL)); |
| +unmap_input_error: |
| + gst_buffer_unmap (input, &map_info); |
| + gst_buffer_unref (input); |
| +error: |
| + return GST_FLOW_ERROR; |
| } |
| |
| static GstStateChangeReturn |
| diff --git a/gst/flx/gstflxdec.h b/gst/flx/gstflxdec.h |
| index 3f9a0aa..4fd8dfd 100644 |
| --- a/gst/flx/gstflxdec.h |
| +++ b/gst/flx/gstflxdec.h |
| @@ -23,6 +23,8 @@ |
| #include <gst/gst.h> |
| |
| #include <gst/base/gstadapter.h> |
| +#include <gst/base/gstbytereader.h> |
| +#include <gst/base/gstbytewriter.h> |
| #include "flx_color.h" |
| |
| G_BEGIN_DECLS |
| @@ -45,7 +47,7 @@ struct _GstFlxDec { |
| |
| guint8 *delta_data, *frame_data; |
| GstAdapter *adapter; |
| - gulong size; |
| + gsize size; |
| GstFlxDecState state; |
| gint64 frame_time; |
| gint64 next_time; |
| -- |
| 2.10.2 |
| |