This merges reading of 8bpp and 15/16-bpp pixel formats, uses bytestream
functions to simplify decoding, and does proper bounds checking.
Also, the initial frame cannot be assumed to be initialized, so this clears
the frame with black pixels before reading the first frame and after seeking.
---
libavcodec/flicvideo.c | 867 +++++++++++++++++-------------------------------
1 files changed, 311 insertions(+), 556 deletions(-)
diff --git a/libavcodec/flicvideo.c b/libavcodec/flicvideo.c
index 8e8a813..ec5cc70 100644
--- a/libavcodec/flicvideo.c
+++ b/libavcodec/flicvideo.c
@@ -39,8 +39,10 @@
#include <stdlib.h>
#include <string.h>
-#include "libavutil/intreadwrite.h"
+#include "libavutil/imgutils.h"
#include "avcodec.h"
+#include "bytestream.h"
+#include "mathops.h"
#define FLI_256_COLOR 4
#define FLI_DELTA 7
@@ -59,13 +61,6 @@
#define FLC_DTA_TYPE_CODE (0xAF44) /* Marks an "Extended FLC" comes from
Dave's Targa Animator (DTA) */
#define FLC_MAGIC_CARPET_SYNTHETIC_TYPE_CODE (0xAF13)
-#define CHECK_PIXEL_PTR(n) \
- if (pixel_ptr + n > pixel_limit) { \
- av_log (s->avctx, AV_LOG_INFO, "Problem: pixel_ptr >= pixel_limit (%d
>= %d)\n", \
- pixel_ptr + n, pixel_limit); \
- return -1; \
- } \
-
typedef struct FlicDecodeContext {
AVCodecContext *avctx;
AVFrame frame;
@@ -73,6 +68,8 @@ typedef struct FlicDecodeContext {
unsigned int palette[256];
int new_palette;
int fli_type; /* either 0xAF11 or 0xAF12, affects palette resolution */
+ int bpp;
+ int cleared_frame;
} FlicDecodeContext;
static av_cold int flic_decode_init(AVCodecContext *avctx)
@@ -91,7 +88,6 @@ static av_cold int flic_decode_init(AVCodecContext *avctx)
s->fli_type = AV_RL16(&fli_header[4]); /* Might be overridden if a Magic
Carpet FLC */
- depth = 0;
if (s->avctx->extradata_size == 12) {
/* special case for magic carpet FLIs */
s->fli_type = FLC_MAGIC_CARPET_SYNTHETIC_TYPE_CODE;
@@ -110,8 +106,8 @@ static av_cold int flic_decode_init(AVCodecContext *avctx)
switch (depth) {
case 8 : avctx->pix_fmt = PIX_FMT_PAL8; break;
- case 15 : avctx->pix_fmt = PIX_FMT_RGB555; break;
- case 16 : avctx->pix_fmt = PIX_FMT_RGB565; break;
+ case 15 : avctx->pix_fmt = PIX_FMT_RGB555LE; break;
+ case 16 : avctx->pix_fmt = PIX_FMT_RGB565LE; break;
case 24 : avctx->pix_fmt = PIX_FMT_BGR24; /* Supposedly BGR, but
havent any files to test with */
av_log(avctx, AV_LOG_ERROR, "24Bpp FLC/FLX is unsupported
due to no test files.\n");
return -1;
@@ -119,6 +115,7 @@ static av_cold int flic_decode_init(AVCodecContext *avctx)
av_log(avctx, AV_LOG_ERROR, "Unknown FLC/FLX depth of %d Bpp
is unsupported.\n",depth);
return -1;
}
+ s->bpp = depth;
s->frame.data[0] = NULL;
s->new_palette = 0;
@@ -126,340 +123,282 @@ static av_cold int flic_decode_init(AVCodecContext
*avctx)
return 0;
}
-static int flic_decode_frame_8BPP(AVCodecContext *avctx,
- void *data, int *data_size,
- const uint8_t *buf, int buf_size)
+#define CHECK_BUFFER_SIZE(n) do { \
+ if (buf_end - buf < (n)) { \
+ av_log(s->avctx, AV_LOG_ERROR, "chunk data overflow\n"); \
+ return AVERROR_INVALIDDATA; \
+ } \
+} while (0)
+
+#define CHECK_IMAGE_SIZE(n) do { \
+ if (pixels_end - pixels < (n)) { \
+ av_log(s->avctx, AV_LOG_ERROR, "image buffer overflow\n"); \
+ return AVERROR_INVALIDDATA; \
+ } \
+} while (0)
+
+static int flic_decode_palette(FlicDecodeContext *s, int chunk_type,
+ const uint8_t *buf, const uint8_t *buf_end)
{
- FlicDecodeContext *s = avctx->priv_data;
-
- int stream_ptr = 0;
- int stream_ptr_after_color_chunk;
- int pixel_ptr;
- int palette_ptr;
- unsigned char palette_idx1;
- unsigned char palette_idx2;
-
- unsigned int frame_size;
- int num_chunks;
-
- unsigned int chunk_size;
- int chunk_type;
-
int i, j;
+ int color_packets, color_changes, color_shift;
+ unsigned int *palette;
- int color_packets;
- int color_changes;
- int color_shift;
- unsigned char r, g, b;
-
- int lines;
- int compressed_lines;
- int starting_line;
- signed short line_packets;
- int y_ptr;
- int byte_run;
- int pixel_skip;
- int pixel_countdown;
- unsigned char *pixels;
- unsigned int pixel_limit;
-
- s->frame.reference = 1;
- s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE |
FF_BUFFER_HINTS_REUSABLE;
- if (avctx->reget_buffer(avctx, &s->frame) < 0) {
- av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
- return -1;
+ if (s->bpp != 8) {
+ av_log(s->avctx, AV_LOG_ERROR, "Unexpected palette chunk in
non-paletised FLC\n");
+ /* skip the chunk */
+ return 0;
}
- pixels = s->frame.data[0];
- pixel_limit = s->avctx->height * s->frame.linesize[0];
-
- frame_size = AV_RL32(&buf[stream_ptr]);
- stream_ptr += 6; /* skip the magic number */
- num_chunks = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 10; /* skip padding */
-
- frame_size -= 16;
-
- /* iterate through the chunks */
- while ((frame_size > 0) && (num_chunks > 0)) {
- chunk_size = AV_RL32(&buf[stream_ptr]);
- stream_ptr += 4;
- chunk_type = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
-
- switch (chunk_type) {
- case FLI_256_COLOR:
- case FLI_COLOR:
- stream_ptr_after_color_chunk = stream_ptr + chunk_size - 6;
-
- /* check special case: If this file is from the Magic Carpet
- * game and uses 6-bit colors even though it reports 256-color
- * chunks in a 0xAF12-type file (fli_type is set to 0xAF13 during
- * initialization) */
- if ((chunk_type == FLI_256_COLOR) && (s->fli_type !=
FLC_MAGIC_CARPET_SYNTHETIC_TYPE_CODE))
- color_shift = 0;
- else
- color_shift = 2;
- /* set up the palette */
- color_packets = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- palette_ptr = 0;
- for (i = 0; i < color_packets; i++) {
- /* first byte is how many colors to skip */
- palette_ptr += buf[stream_ptr++];
-
- /* next byte indicates how many entries to change */
- color_changes = buf[stream_ptr++];
-
- /* if there are 0 color changes, there are actually 256 */
- if (color_changes == 0)
- color_changes = 256;
-
- for (j = 0; j < color_changes; j++) {
- unsigned int entry;
-
- /* wrap around, for good measure */
- if ((unsigned)palette_ptr >= 256)
- palette_ptr = 0;
-
- r = buf[stream_ptr++] << color_shift;
- g = buf[stream_ptr++] << color_shift;
- b = buf[stream_ptr++] << color_shift;
- entry = (r << 16) | (g << 8) | b;
- if (s->palette[palette_ptr] != entry)
- s->new_palette = 1;
- s->palette[palette_ptr++] = entry;
- }
- }
+ /* check special case: If this file is from the Magic Carpet
+ * game and uses 6-bit colors even though it reports 256-color
+ * chunks in a 0xAF12-type file (fli_type is set to 0xAF13 during
+ * initialization) */
+ if (chunk_type == FLI_256_COLOR &&
+ s->fli_type != FLC_MAGIC_CARPET_SYNTHETIC_TYPE_CODE)
+ color_shift = 0;
+ else
+ color_shift = 2;
+
+ /* set up the palette */
+ palette = s->palette;
+ CHECK_BUFFER_SIZE(2);
+ color_packets = bytestream_get_le16(&buf);
+
+ for (i = 0; i < color_packets; i++) {
+ CHECK_BUFFER_SIZE(2);
+ /* skip palette entries*/
+ palette += bytestream_get_byte(&buf);
+ /* how many entries to change */
+ color_changes = bytestream_get_byte(&buf);
+ /* if there are 0 color changes, there are actually 256 */
+ if (color_changes == 0)
+ color_changes = 256;
+
+ CHECK_BUFFER_SIZE(color_changes * 3);
+ for (j = 0; j < color_changes; j++) {
+ unsigned int entry;
+
+ /* wrap around, for good measure */
+ if (palette >= s->palette + 256)
+ palette = s->palette;
+
+ entry = (0xFFU << 24) | (bytestream_get_be24(&buf) << color_shift);
+ if (*palette != entry)
+ s->new_palette = 1;
+ *palette++ = entry;
+ }
+ }
- /* color chunks sometimes have weird 16-bit alignment issues;
- * therefore, take the hardline approach and set the stream_ptr
- * to the value calculated w.r.t. the size specified by the color
- * chunk header */
- stream_ptr = stream_ptr_after_color_chunk;
+ return 0;
+}
+static int flic_decode_delta(FlicDecodeContext *s, int chunk_type,
+ const uint8_t *buf, const uint8_t *buf_end)
+{
+ uint8_t *y_ptr;
+ int i, j, compressed_lines, line_packets, col_skip, byte_run, pixel;
+ uint8_t *pixels = s->frame.data[0];
+ uint8_t *pixels_end = pixels + s->avctx->height * s->frame.linesize[0];
+ int pixel_size = (s->bpp + 1) / 8;
+
+ y_ptr = pixels;
+ CHECK_BUFFER_SIZE(2);
+ compressed_lines = bytestream_get_le16(&buf);
+ while (compressed_lines > 0 && y_ptr < pixels_end) {
+ CHECK_BUFFER_SIZE(2);
+ line_packets = sign_extend(bytestream_get_le16(&buf), 16);
+ if ((pixel_size == 1 && (line_packets & 0xC000) == 0xC000) ||
+ (pixel_size > 1 && line_packets < 0)) {
+ // line skip opcode
+ line_packets = -line_packets;
+ CHECK_IMAGE_SIZE(line_packets * s->frame.linesize[0]);
+ y_ptr += line_packets * s->frame.linesize[0];
+ } else if (pixel_size == 1 && (line_packets & 0xC000) == 0x4000) {
+ av_log(s->avctx, AV_LOG_ERROR, "Undefined opcode (%x) in
DELTA_FLI\n",
+ line_packets);
+ return AVERROR_INVALIDDATA;
+ } else if (pixel_size == 1 && (line_packets & 0xC000) == 0x8000) {
+ // "last byte" opcode
+ pixels = y_ptr + s->frame.linesize[0] - 1;
+ CHECK_IMAGE_SIZE(1);
+ *pixels = line_packets & 0xff;
break;
-
- case FLI_DELTA:
- y_ptr = 0;
- compressed_lines = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- while (compressed_lines > 0) {
- line_packets = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- if ((line_packets & 0xC000) == 0xC000) {
- // line skip opcode
- line_packets = -line_packets;
- y_ptr += line_packets * s->frame.linesize[0];
- } else if ((line_packets & 0xC000) == 0x4000) {
- av_log(avctx, AV_LOG_ERROR, "Undefined opcode (%x) in
DELTA_FLI\n", line_packets);
- } else if ((line_packets & 0xC000) == 0x8000) {
- // "last byte" opcode
- pixel_ptr= y_ptr + s->frame.linesize[0] - 1;
- CHECK_PIXEL_PTR(0);
- pixels[pixel_ptr] = line_packets & 0xff;
- } else {
- compressed_lines--;
- pixel_ptr = y_ptr;
- CHECK_PIXEL_PTR(0);
- pixel_countdown = s->avctx->width;
- for (i = 0; i < line_packets; i++) {
- /* account for the skip bytes */
- pixel_skip = buf[stream_ptr++];
- pixel_ptr += pixel_skip;
- pixel_countdown -= pixel_skip;
- byte_run = (signed char)(buf[stream_ptr++]);
- if (byte_run < 0) {
- byte_run = -byte_run;
- palette_idx1 = buf[stream_ptr++];
- palette_idx2 = buf[stream_ptr++];
- CHECK_PIXEL_PTR(byte_run * 2);
- for (j = 0; j < byte_run; j++, pixel_countdown -=
2) {
- pixels[pixel_ptr++] = palette_idx1;
- pixels[pixel_ptr++] = palette_idx2;
- }
- } else {
- CHECK_PIXEL_PTR(byte_run * 2);
- for (j = 0; j < byte_run * 2; j++,
pixel_countdown--) {
- palette_idx1 = buf[stream_ptr++];
- pixels[pixel_ptr++] = palette_idx1;
- }
- }
+ } else {
+ compressed_lines--;
+ pixels = y_ptr;
+ for (i = 0; i < line_packets; i++) {
+ CHECK_BUFFER_SIZE(2);
+ col_skip = bytestream_get_byte(&buf);
+ CHECK_IMAGE_SIZE(col_skip * pixel_size);
+ pixels += col_skip * pixel_size;
+ byte_run = sign_extend(bytestream_get_byte(&buf), 8);
+ CHECK_IMAGE_SIZE(abs(byte_run) * 2);
+ if (byte_run < 0) {
+ byte_run = -byte_run;
+ CHECK_BUFFER_SIZE(2);
+ pixel = bytestream_get_le16(&buf);
+ for (j = 0; j < byte_run; j++) {
+ AV_WL16(pixels, pixel);
+ pixels += 2;
}
-
- y_ptr += s->frame.linesize[0];
+ } else {
+ CHECK_BUFFER_SIZE(byte_run * 2);
+ memcpy(pixels, buf, byte_run * 2);
+ buf += byte_run * 2;
+ pixels += byte_run * 2;
}
}
- break;
+ y_ptr += s->frame.linesize[0];
+ }
+ }
+ return 0;
+}
- case FLI_LC:
- /* line compressed */
- starting_line = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- y_ptr = 0;
- y_ptr += starting_line * s->frame.linesize[0];
-
- compressed_lines = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- while (compressed_lines > 0) {
- pixel_ptr = y_ptr;
- CHECK_PIXEL_PTR(0);
- pixel_countdown = s->avctx->width;
- line_packets = buf[stream_ptr++];
- if (line_packets > 0) {
- for (i = 0; i < line_packets; i++) {
- /* account for the skip bytes */
- pixel_skip = buf[stream_ptr++];
- pixel_ptr += pixel_skip;
- pixel_countdown -= pixel_skip;
- byte_run = (signed char)(buf[stream_ptr++]);
- if (byte_run > 0) {
- CHECK_PIXEL_PTR(byte_run);
- for (j = 0; j < byte_run; j++, pixel_countdown--) {
- palette_idx1 = buf[stream_ptr++];
- pixels[pixel_ptr++] = palette_idx1;
- }
- } else if (byte_run < 0) {
- byte_run = -byte_run;
- palette_idx1 = buf[stream_ptr++];
- CHECK_PIXEL_PTR(byte_run);
- for (j = 0; j < byte_run; j++, pixel_countdown--) {
- pixels[pixel_ptr++] = palette_idx1;
- }
- }
- }
- }
+static int flic_decode_line_compressed(FlicDecodeContext *s, int chunk_type,
+ const uint8_t *buf,
+ const uint8_t *buf_end)
+{
+ uint8_t *y_ptr;
+ int compressed_lines;
+ int line_packets;
+ uint8_t *pixels = s->frame.data[0];
+ uint8_t *pixels_end = pixels + s->avctx->height * s->frame.linesize[0];
+ int line_skip, col_skip, byte_run;
+ int i;
+
+ if (s->bpp != 8) {
+ av_log(s->avctx, AV_LOG_ERROR, "Unexpected FLI_LC chunk in
non-paletised FLC\n");
+ /* skip the chunk */
+ return 0;
+ }
- y_ptr += s->frame.linesize[0];
- compressed_lines--;
+ CHECK_BUFFER_SIZE(4);
+
+ line_skip = bytestream_get_le16(&buf);
+ CHECK_IMAGE_SIZE(line_skip * s->frame.linesize[0]);
+ y_ptr = pixels + line_skip * s->frame.linesize[0];
+
+ compressed_lines = bytestream_get_le16(&buf);
+ while (compressed_lines > 0 && y_ptr < pixels_end) {
+ pixels = y_ptr;
+ CHECK_BUFFER_SIZE(1);
+ line_packets = bytestream_get_byte(&buf);
+ for (i = 0; i < line_packets; i++) {
+ CHECK_BUFFER_SIZE(2);
+ col_skip = bytestream_get_byte(&buf);
+ CHECK_IMAGE_SIZE(col_skip);
+ pixels += col_skip;
+ byte_run = sign_extend(bytestream_get_byte(&buf), 8);
+ CHECK_IMAGE_SIZE(abs(byte_run));
+ if (byte_run < 0) {
+ byte_run = -byte_run;
+ CHECK_BUFFER_SIZE(1);
+ memset(pixels, bytestream_get_byte(&buf), byte_run);
+ pixels += byte_run;
+ } else if (byte_run > 0) {
+ CHECK_BUFFER_SIZE(byte_run);
+ memcpy(pixels, buf, byte_run);
+ pixels += byte_run;
+ buf += byte_run;
}
- break;
-
- case FLI_BLACK:
- /* set the whole frame to color 0 (which is usually black) */
- memset(pixels, 0,
- s->frame.linesize[0] * s->avctx->height);
- break;
+ }
+ y_ptr += s->frame.linesize[0];
+ compressed_lines--;
+ }
+ return 0;
+}
- case FLI_BRUN:
- /* Byte run compression: This chunk type only occurs in the first
- * FLI frame and it will update the entire frame. */
- y_ptr = 0;
- for (lines = 0; lines < s->avctx->height; lines++) {
- pixel_ptr = y_ptr;
- /* disregard the line packets; instead, iterate through all
- * pixels on a row */
- stream_ptr++;
- pixel_countdown = s->avctx->width;
- while (pixel_countdown > 0) {
- byte_run = (signed char)(buf[stream_ptr++]);
- if (byte_run > 0) {
- palette_idx1 = buf[stream_ptr++];
- CHECK_PIXEL_PTR(byte_run);
- for (j = 0; j < byte_run; j++) {
- pixels[pixel_ptr++] = palette_idx1;
- pixel_countdown--;
- if (pixel_countdown < 0)
- av_log(avctx, AV_LOG_ERROR, "pixel_countdown <
0 (%d) at line %d\n",
- pixel_countdown, lines);
- }
- } else { /* copy bytes if byte_run < 0 */
- byte_run = -byte_run;
- CHECK_PIXEL_PTR(byte_run);
- for (j = 0; j < byte_run; j++) {
- palette_idx1 = buf[stream_ptr++];
- pixels[pixel_ptr++] = palette_idx1;
- pixel_countdown--;
- if (pixel_countdown < 0)
- av_log(avctx, AV_LOG_ERROR, "pixel_countdown <
0 (%d) at line %d\n",
- pixel_countdown, lines);
- }
+static int flic_decode_byte_run(FlicDecodeContext *s, int chunk_type,
+ const uint8_t *buf, const uint8_t *buf_end)
+{
+ uint8_t *y_ptr;
+ uint8_t *line_end;
+ uint8_t *pixels = s->frame.data[0];
+ uint8_t *pixels_end = pixels + s->avctx->height * s->frame.linesize[0];
+ int byte_run, pixel;
+ int y, j;
+ /* FLI_DTA_BRUN chunks have pixel runs instead of byte runs */
+ int pixel_size = chunk_type == FLI_DTA_BRUN ? (s->bpp + 1) / 8 : 1;
+
+ y_ptr = pixels;
+ for (y = 0; y < s->avctx->height; y++) {
+ pixels = y_ptr;
+ line_end = y_ptr + s->avctx->width * pixel_size;
+ /* disregard the line packets. instead, iterate through all pixels on
a row */
+ buf++;
+ while (pixels < line_end) {
+ CHECK_BUFFER_SIZE(1);
+ byte_run = sign_extend(bytestream_get_byte(&buf), 8);
+ CHECK_IMAGE_SIZE(abs(byte_run));
+ if (byte_run > 0) {
+ CHECK_BUFFER_SIZE(pixel_size);
+ if (pixel_size == 1) {
+ pixel = bytestream_get_byte(&buf);
+ memset(pixels, pixel, byte_run);
+ pixels += byte_run;
+ } else {
+ pixel = bytestream_get_le16(&buf);
+ for (j = 0; j < byte_run; j++) {
+ AV_WL16(pixels, pixel);
+ pixels += 2;
}
}
-
- y_ptr += s->frame.linesize[0];
- }
- break;
-
- case FLI_COPY:
- /* copy the chunk (uncompressed frame) */
- if (chunk_size - 6 > s->avctx->width * s->avctx->height) {
- av_log(avctx, AV_LOG_ERROR, "In chunk FLI_COPY : source data
(%d bytes) " \
- "bigger than image, skipping chunk\n", chunk_size - 6);
- stream_ptr += chunk_size - 6;
} else {
- for (y_ptr = 0; y_ptr < s->frame.linesize[0] *
s->avctx->height;
- y_ptr += s->frame.linesize[0]) {
- memcpy(&pixels[y_ptr], &buf[stream_ptr],
- s->avctx->width);
- stream_ptr += s->avctx->width;
- }
+ byte_run = -byte_run;
+ CHECK_BUFFER_SIZE(byte_run * pixel_size);
+ memcpy(pixels, buf, byte_run * pixel_size);
+ pixels += byte_run * pixel_size;
+ buf += byte_run * pixel_size;
}
- break;
-
- case FLI_MINI:
- /* some sort of a thumbnail? disregard this chunk... */
- stream_ptr += chunk_size - 6;
- break;
-
- default:
- av_log(avctx, AV_LOG_ERROR, "Unrecognized chunk type: %d\n",
chunk_type);
- break;
}
-
- frame_size -= chunk_size;
- num_chunks--;
+ y_ptr += s->frame.linesize[0];
}
+ return 0;
+}
- /* by the end of the chunk, the stream ptr should equal the frame
- * size (minus 1, possibly); if it doesn't, issue a warning */
- if ((stream_ptr != buf_size) && (stream_ptr != buf_size - 1))
- av_log(avctx, AV_LOG_ERROR, "Processed FLI chunk where chunk size = %d
" \
- "and final chunk ptr = %d\n", buf_size, stream_ptr);
-
- /* make the palette available on the way out */
- memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE);
- if (s->new_palette) {
- s->frame.palette_has_changed = 1;
- s->new_palette = 0;
+static int flic_decode_copy(FlicDecodeContext *s, int chunk_type,
+ const uint8_t *buf, const uint8_t *buf_end)
+{
+ int line_size, copy_height, copy_width;
+ int pixel_size = (s->bpp + 1) / 8;
+
+ /* Some files have a COPY chunk size smaller than the full image.
+ It is unclear if these are invalid files or how they should be handled.
+ We're just going to leave the remaining data from the previous frame. */
+ line_size = s->avctx->width * pixel_size;
+ copy_height = (buf_end - buf) / line_size;
+ copy_height = FFMIN(copy_height, s->avctx->height);
+ copy_width = (buf_end - buf) % line_size;
+
+ av_image_copy_plane(s->frame.data[0], s->frame.linesize[0], buf,
+ line_size, line_size, copy_height);
+
+ if (copy_height < s->avctx->height && copy_width > 0) {
+ memcpy(s->frame.data[0] + s->frame.linesize[0] * copy_height,
+ buf + s->avctx->width * copy_height,
+ copy_width);
}
- *data_size=sizeof(AVFrame);
- *(AVFrame*)data = s->frame;
-
- return buf_size;
+ return 0;
}
-static int flic_decode_frame_15_16BPP(AVCodecContext *avctx,
- void *data, int *data_size,
- const uint8_t *buf, int buf_size)
+static int flic_decode_black(FlicDecodeContext *s, int chunk_type,
+ const uint8_t *buf, const uint8_t *buf_end)
{
- /* Note, the only difference between the 15Bpp and 16Bpp */
- /* Format is the pixel format, the packets are processed the same. */
- FlicDecodeContext *s = avctx->priv_data;
-
- int stream_ptr = 0;
- int pixel_ptr;
- unsigned char palette_idx1;
-
- unsigned int frame_size;
- int num_chunks;
-
- unsigned int chunk_size;
- int chunk_type;
-
- int i, j;
+ memset(s->frame.data[0], 0, s->frame.linesize[0] * s->avctx->height);
+ return 0;
+}
- int lines;
- int compressed_lines;
- signed short line_packets;
- int y_ptr;
- int byte_run;
- int pixel_skip;
- int pixel_countdown;
- unsigned char *pixels;
- int pixel;
- unsigned int pixel_limit;
+static int flic_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
+ AVPacket *avpkt)
+{
+ FlicDecodeContext *s = avctx->priv_data;
+ int buf_size = avpkt->size;
+ const uint8_t *buf = avpkt->data;
+ const uint8_t *buf_end = buf + buf_size;
+ unsigned int frame_size, chunk_size;
+ int num_chunks, chunk_type, i, ret;
s->frame.reference = 1;
s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE |
FF_BUFFER_HINTS_REUSABLE;
@@ -467,229 +406,70 @@ static int flic_decode_frame_15_16BPP(AVCodecContext
*avctx,
av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
return -1;
}
+ if (!s->cleared_frame) {
+ memset(s->frame.data[0], 0, s->frame.linesize[0] * s->avctx->height);
+ s->cleared_frame = 1;
+ }
- pixels = s->frame.data[0];
- pixel_limit = s->avctx->height * s->frame.linesize[0];
-
- frame_size = AV_RL32(&buf[stream_ptr]);
- stream_ptr += 6; /* skip the magic number */
- num_chunks = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 10; /* skip padding */
-
+ CHECK_BUFFER_SIZE(16);
+ frame_size = bytestream_get_le32(&buf);
+ frame_size = FFMIN(frame_size, buf_size);
+ buf += 2; /* skip the magic number */
+ num_chunks = bytestream_get_le16(&buf);
+ buf += 8; /* skip padding */
frame_size -= 16;
/* iterate through the chunks */
- while ((frame_size > 0) && (num_chunks > 0)) {
- chunk_size = AV_RL32(&buf[stream_ptr]);
- stream_ptr += 4;
- chunk_type = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
+ for (i = 0; i < num_chunks; i++) {
+ int (*decode_chunk)(FlicDecodeContext *s, int chunk_type,
+ const uint8_t *buf, const uint8_t *buf_end) = NULL;
+
+ CHECK_BUFFER_SIZE(6);
+ chunk_size = bytestream_get_le32(&buf);
+ if (chunk_size > frame_size) {
+ av_log(avctx, AV_LOG_WARNING, "Invalid chunk size > frame size "
+ "(%u > %u)\n", chunk_size, frame_size);
+ chunk_size = frame_size;
+ }
+ if (chunk_size < 6) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid chunk size: %u\n",
chunk_size);
+ return AVERROR_INVALIDDATA;
+ }
+ chunk_type = bytestream_get_le16(&buf);
+ av_dlog(s->avctx, "Chunk: type=%d size=%u\n", chunk_type, chunk_size);
switch (chunk_type) {
case FLI_256_COLOR:
- case FLI_COLOR:
- /* For some reason, it seems that non-palettized flics do
- * include one of these chunks in their first frame.
- * Why I do not know, it seems rather extraneous. */
-/* av_log(avctx, AV_LOG_ERROR, "Unexpected Palette chunk %d in
non-paletised FLC\n",chunk_type);*/
- stream_ptr = stream_ptr + chunk_size - 6;
- break;
-
+ case FLI_COLOR: decode_chunk = flic_decode_palette; break;
case FLI_DELTA:
- case FLI_DTA_LC:
- y_ptr = 0;
- compressed_lines = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- while (compressed_lines > 0) {
- line_packets = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- if (line_packets < 0) {
- line_packets = -line_packets;
- y_ptr += line_packets * s->frame.linesize[0];
- } else {
- compressed_lines--;
- pixel_ptr = y_ptr;
- CHECK_PIXEL_PTR(0);
- pixel_countdown = s->avctx->width;
- for (i = 0; i < line_packets; i++) {
- /* account for the skip bytes */
- pixel_skip = buf[stream_ptr++];
- pixel_ptr += (pixel_skip*2); /* Pixel is 2 bytes wide
*/
- pixel_countdown -= pixel_skip;
- byte_run = (signed char)(buf[stream_ptr++]);
- if (byte_run < 0) {
- byte_run = -byte_run;
- pixel = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- CHECK_PIXEL_PTR(2 * byte_run);
- for (j = 0; j < byte_run; j++, pixel_countdown -=
2) {
- *((signed short*)(&pixels[pixel_ptr])) = pixel;
- pixel_ptr += 2;
- }
- } else {
- CHECK_PIXEL_PTR(2 * byte_run);
- for (j = 0; j < byte_run; j++, pixel_countdown--) {
- *((signed short*)(&pixels[pixel_ptr])) =
AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- pixel_ptr += 2;
- }
- }
- }
-
- y_ptr += s->frame.linesize[0];
- }
- }
- break;
-
- case FLI_LC:
- av_log(avctx, AV_LOG_ERROR, "Unexpected FLI_LC chunk in
non-paletised FLC\n");
- stream_ptr = stream_ptr + chunk_size - 6;
- break;
-
- case FLI_BLACK:
- /* set the whole frame to 0x0000 which is black in both 15Bpp and
16Bpp modes. */
- memset(pixels, 0x0000,
- s->frame.linesize[0] * s->avctx->height);
- break;
-
+ case FLI_DTA_LC: decode_chunk = flic_decode_delta; break;
+ case FLI_LC: decode_chunk = flic_decode_line_compressed; break;
+ case FLI_BLACK: decode_chunk = flic_decode_black; break;
case FLI_BRUN:
- y_ptr = 0;
- for (lines = 0; lines < s->avctx->height; lines++) {
- pixel_ptr = y_ptr;
- /* disregard the line packets; instead, iterate through all
- * pixels on a row */
- stream_ptr++;
- pixel_countdown = (s->avctx->width * 2);
-
- while (pixel_countdown > 0) {
- byte_run = (signed char)(buf[stream_ptr++]);
- if (byte_run > 0) {
- palette_idx1 = buf[stream_ptr++];
- CHECK_PIXEL_PTR(byte_run);
- for (j = 0; j < byte_run; j++) {
- pixels[pixel_ptr++] = palette_idx1;
- pixel_countdown--;
- if (pixel_countdown < 0)
- av_log(avctx, AV_LOG_ERROR, "pixel_countdown <
0 (%d) (linea%d)\n",
- pixel_countdown, lines);
- }
- } else { /* copy bytes if byte_run < 0 */
- byte_run = -byte_run;
- CHECK_PIXEL_PTR(byte_run);
- for (j = 0; j < byte_run; j++) {
- palette_idx1 = buf[stream_ptr++];
- pixels[pixel_ptr++] = palette_idx1;
- pixel_countdown--;
- if (pixel_countdown < 0)
- av_log(avctx, AV_LOG_ERROR, "pixel_countdown <
0 (%d) at line %d\n",
- pixel_countdown, lines);
- }
- }
- }
-
- /* Now FLX is strange, in that it is "byte" as opposed to
"pixel" run length compressed.
- * This does not give us any good oportunity to perform word
endian conversion
- * during decompression. So if it is required (i.e., this is
not a LE target, we do
- * a second pass over the line here, swapping the bytes.
- */
-#if HAVE_BIGENDIAN
- pixel_ptr = y_ptr;
- pixel_countdown = s->avctx->width;
- while (pixel_countdown > 0) {
- *((signed short*)(&pixels[pixel_ptr])) =
AV_RL16(&buf[pixel_ptr]);
- pixel_ptr += 2;
- }
-#endif
- y_ptr += s->frame.linesize[0];
- }
- break;
-
- case FLI_DTA_BRUN:
- y_ptr = 0;
- for (lines = 0; lines < s->avctx->height; lines++) {
- pixel_ptr = y_ptr;
- /* disregard the line packets; instead, iterate through all
- * pixels on a row */
- stream_ptr++;
- pixel_countdown = s->avctx->width; /* Width is in pixels, not
bytes */
-
- while (pixel_countdown > 0) {
- byte_run = (signed char)(buf[stream_ptr++]);
- if (byte_run > 0) {
- pixel = AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- CHECK_PIXEL_PTR(2 * byte_run);
- for (j = 0; j < byte_run; j++) {
- *((signed short*)(&pixels[pixel_ptr])) = pixel;
- pixel_ptr += 2;
- pixel_countdown--;
- if (pixel_countdown < 0)
- av_log(avctx, AV_LOG_ERROR, "pixel_countdown <
0 (%d)\n",
- pixel_countdown);
- }
- } else { /* copy pixels if byte_run < 0 */
- byte_run = -byte_run;
- CHECK_PIXEL_PTR(2 * byte_run);
- for (j = 0; j < byte_run; j++) {
- *((signed short*)(&pixels[pixel_ptr])) =
AV_RL16(&buf[stream_ptr]);
- stream_ptr += 2;
- pixel_ptr += 2;
- pixel_countdown--;
- if (pixel_countdown < 0)
- av_log(avctx, AV_LOG_ERROR, "pixel_countdown <
0 (%d)\n",
- pixel_countdown);
- }
- }
- }
-
- y_ptr += s->frame.linesize[0];
- }
- break;
-
+ case FLI_DTA_BRUN: decode_chunk = flic_decode_byte_run; break;
case FLI_COPY:
- case FLI_DTA_COPY:
- /* copy the chunk (uncompressed frame) */
- if (chunk_size - 6 > (unsigned int)(s->avctx->width *
s->avctx->height)*2) {
- av_log(avctx, AV_LOG_ERROR, "In chunk FLI_COPY : source data
(%d bytes) " \
- "bigger than image, skipping chunk\n", chunk_size - 6);
- stream_ptr += chunk_size - 6;
- } else {
-
- for (y_ptr = 0; y_ptr < s->frame.linesize[0] *
s->avctx->height;
- y_ptr += s->frame.linesize[0]) {
-
- pixel_countdown = s->avctx->width;
- pixel_ptr = 0;
- while (pixel_countdown > 0) {
- *((signed short*)(&pixels[y_ptr + pixel_ptr])) =
AV_RL16(&buf[stream_ptr+pixel_ptr]);
- pixel_ptr += 2;
- pixel_countdown--;
- }
- stream_ptr += s->avctx->width*2;
- }
- }
- break;
-
- case FLI_MINI:
- /* some sort of a thumbnail? disregard this chunk... */
- stream_ptr += chunk_size - 6;
- break;
-
- default:
- av_log(avctx, AV_LOG_ERROR, "Unrecognized chunk type: %d\n",
chunk_type);
- break;
+ case FLI_DTA_COPY: decode_chunk = flic_decode_copy; break;
+ }
+ if (decode_chunk) {
+ if ((ret = decode_chunk(s, chunk_type, buf, &buf[chunk_size - 6])))
+ return ret;
+ } else {
+ av_log(avctx, AV_LOG_WARNING, "Skipping chunk: type=%d size=%u\n",
+ chunk_type, chunk_size);
}
+ buf += chunk_size - 6;
frame_size -= chunk_size;
- num_chunks--;
}
- /* by the end of the chunk, the stream ptr should equal the frame
- * size (minus 1, possibly); if it doesn't, issue a warning */
- if ((stream_ptr != buf_size) && (stream_ptr != buf_size - 1))
- av_log(avctx, AV_LOG_ERROR, "Processed FLI chunk where chunk size = %d
" \
- "and final chunk ptr = %d\n", buf_size, stream_ptr);
-
+ /* make the palette available on the way out */
+ if (s->bpp == 8) {
+ memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE);
+ if (s->new_palette) {
+ s->frame.palette_has_changed = 1;
+ s->new_palette = 0;
+ }
+ }
*data_size=sizeof(AVFrame);
*(AVFrame*)data = s->frame;
@@ -697,40 +477,14 @@ static int flic_decode_frame_15_16BPP(AVCodecContext
*avctx,
return buf_size;
}
-static int flic_decode_frame_24BPP(AVCodecContext *avctx,
- void *data, int *data_size,
- const uint8_t *buf, int buf_size)
+static av_cold void flic_decode_flush(AVCodecContext *avctx)
{
- av_log(avctx, AV_LOG_ERROR, "24Bpp FLC Unsupported due to lack of test
files.\n");
- return -1;
-}
+ FlicDecodeContext *s = avctx->priv_data;
-static int flic_decode_frame(AVCodecContext *avctx,
- void *data, int *data_size,
- AVPacket *avpkt)
-{
- const uint8_t *buf = avpkt->data;
- int buf_size = avpkt->size;
- if (avctx->pix_fmt == PIX_FMT_PAL8) {
- return flic_decode_frame_8BPP(avctx, data, data_size,
- buf, buf_size);
- }
- else if ((avctx->pix_fmt == PIX_FMT_RGB555) ||
- (avctx->pix_fmt == PIX_FMT_RGB565)) {
- return flic_decode_frame_15_16BPP(avctx, data, data_size,
- buf, buf_size);
- }
- else if (avctx->pix_fmt == PIX_FMT_BGR24) {
- return flic_decode_frame_24BPP(avctx, data, data_size,
- buf, buf_size);
- }
+ if (s->frame.data[0])
+ avctx->release_buffer(avctx, &s->frame);
- /* Should not get here, ever as the pix_fmt is processed */
- /* in flic_decode_init and the above if should deal with */
- /* the finite set of possibilites allowable by here. */
- /* But in case we do, just error out. */
- av_log(avctx, AV_LOG_ERROR, "Unknown FLC format, my science cannot explain
how this happened.\n");
- return -1;
+ s->cleared_frame = 0;
}
@@ -752,6 +506,7 @@ AVCodec ff_flic_decoder = {
.init = flic_decode_init,
.close = flic_decode_end,
.decode = flic_decode_frame,
+ .flush = flic_decode_flush,
.capabilities = CODEC_CAP_DR1,
.long_name = NULL_IF_CONFIG_SMALL("Autodesk Animator Flic video"),
};
--
1.7.1
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel