[Ffmpeg-devel] Enhance FLI/FLC Decoder to support FLX type files.
Steven Johnson
mplayer
Fri Oct 7 23:06:47 CEST 2005
Hi All,
Attached is my first patch against FFMPEG.
It provides for support of 15bpp FLX files (as generated) by Ulead Media
Studio Pro, and probably other programs.
It provides for support of the 15bpp/16bpp better compressed FLX files,
with the extensions created by Daves Targa Animator [DTA] (a program
from the early 90's).
Ive tested it against FLX's from Ulead. And FLX with DTA extensions
from EGI V4.00. (The only current programs I know that generate these
files).
The next step, after this is accepted is to integrate encoding of
FLC(8Bpp), FLX (15Bpp), FLX+DTA(15/16/24Bpp) files.
Why would one do this. Well its not the worlds best codec, thats for
sure. But it is very easy to implement decoders. Those decoders
require very little in the way of CPU performance, and so it is an ideal
format for small embedded systems with small resolution LCD/LED Displays
(Where i primarily use them). Also, at 24Bpp it is a very fast (but
somewhat compressed) Lossless codec. Also FLC/FLX Excels with cartoon
style animation, where there are large chunks of the same colour, and it
doesnt introduce any noise artifacts at edges, for these types of files.
Note, I havent implemented 24Bpp in the Decoder yet, because i havent
ever seen a 24Bpp FLX. But the FLX format supports it, so when i do the
encoder, ill implement it then.
Ive read the MPlayer document on patches, and i believe mine complies
with the requirements.
Comments appreciated.
Steven
diff -Naur ffmpeg-cvs/libavcodec/flicvideo.c
ffmpeg-edits/libavcodec/flicvideo.c
--- ffmpeg-cvs/libavcodec/flicvideo.c 2005-08-14 03:59:01.000000000 +1100
+++ ffmpeg-edits/libavcodec/flicvideo.c 2005-10-07 11:04:39.000000000
+1100
@@ -26,7 +26,8 @@
* variations, visit:
* http://www.compuphase.com/flic.htm
*
- * This decoder outputs PAL8 colorspace data. To use this decoder, be
+ * This decoder outputs PAL8/RGB555/RGB565 and maybe one day RGB24
+ * colorspace data, depending on the FLC. To use this decoder, be
* sure that your demuxer sends the FLI file header to the decoder via
* the extradata chunk in AVCodecContext. The chunk should be 128 bytes
* large. The only exception is for FLI files from the game "Magic Carpet",
@@ -50,6 +51,14 @@
#define FLI_BRUN 15
#define FLI_COPY 16
#define FLI_MINI 18
+#define FLI_DTA_BRUN 25
+#define FLI_DTA_COPY 26
+#define FLI_DTA_LC 27
+
+#define FLI_TYPE_CODE (0xAF11)
+#define FLC_FLX_TYPE_CODE (0xAF12)
+#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) { \
@@ -71,30 +80,52 @@
{
FlicDecodeContext *s = (FlicDecodeContext *)avctx->priv_data;
unsigned char *fli_header = (unsigned char *)avctx->extradata;
+ int depth;
s->avctx = avctx;
- avctx->pix_fmt = PIX_FMT_PAL8;
avctx->has_b_frames = 0;
+ s->fli_type = LE_16(&fli_header[4]); /* Might be overridden if a
Magic Carpet FLC */
+ depth = LE_16(&fli_header[12]);
+
+ if (depth == 0) {
+ depth = 8; /* Some FLC generators set depth to zero, when they
mean 8Bpp. Fix up here */
+ }
+
if (s->avctx->extradata_size == 12) {
/* special case for magic carpet FLIs */
- s->fli_type = 0xAF13;
- } else if (s->avctx->extradata_size == 128) {
- s->fli_type = LE_16(&fli_header[4]);
- } else {
+ s->fli_type = FLC_MAGIC_CARPET_SYNTHETIC_TYPE_CODE;
+ } else if (s->avctx->extradata_size != 128) {
av_log(avctx, AV_LOG_ERROR, "Expected extradata of 12 or 128
bytes\n");
return -1;
}
+ if ((s->fli_type == FLC_FLX_TYPE_CODE) && (depth == 16)) {
+ depth = 15; /* Original Autodesk FLX's say the depth is 16Bpp
when it is really 15Bpp */
+ }
+
+ 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 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;
+ break;
+ default :
+ av_log(avctx, AV_LOG_ERROR, "Unkown FLC/FLX depth of
%d Bpp is unsupported.\n",depth);
+ return -1;
+ }
+
s->frame.data[0] = NULL;
s->new_palette = 0;
return 0;
}
-static int flic_decode_frame(AVCodecContext *avctx,
- void *data, int *data_size,
- uint8_t *buf, int buf_size)
+static int flic_decode_frame_8BPP(AVCodecContext *avctx,
+ void *data, int *data_size,
+ uint8_t *buf, int buf_size)
{
FlicDecodeContext *s = (FlicDecodeContext *)avctx->priv_data;
@@ -128,7 +159,7 @@
int pixel_countdown;
unsigned char *pixels;
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) {
@@ -163,7 +194,7 @@
* 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 != 0xAF13))
+ if ((chunk_type == FLI_256_COLOR) && (s->fli_type !=
FLC_MAGIC_CARPET_SYNTHETIC_TYPE_CODE))
color_shift = 0;
else
color_shift = 2;
@@ -384,6 +415,308 @@
return buf_size;
}
+int flic_decode_frame_15_16BPP(AVCodecContext *avctx,
+ void *data, int *data_size,
+ uint8_t *buf, int buf_size)
+{
+ /* Note, the only difference between the 15Bpp and 16Bpp */
+ /* Format is the pixel format, the packets are processed the same. */
+ FlicDecodeContext *s = (FlicDecodeContext *)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;
+
+ int lines;
+ int compressed_lines;
+ signed short line_packets;
+ int y_ptr;
+ signed char byte_run;
+ int pixel_skip;
+ int pixel_countdown;
+ unsigned char *pixels;
+ int pixel;
+ 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;
+ }
+
+ pixels = s->frame.data[0];
+ pixel_limit = s->avctx->height * s->frame.linesize[0];
+
+ frame_size = LE_32(&buf[stream_ptr]);
+ stream_ptr += 6; /* skip the magic number */
+ num_chunks = LE_16(&buf[stream_ptr]);
+ stream_ptr += 10; /* skip padding */
+
+ frame_size -= 16;
+
+ /* iterate through the chunks */
+ while ((frame_size > 0) && (num_chunks > 0)) {
+ chunk_size = LE_32(&buf[stream_ptr]);
+ stream_ptr += 4;
+ chunk_type = LE_16(&buf[stream_ptr]);
+ stream_ptr += 2;
+
+ switch (chunk_type) {
+ case FLI_256_COLOR:
+ case FLI_COLOR:
+ /* For some reason, it seems that non-paletised 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_DELTA:
+ case FLI_DTA_LC:
+ y_ptr = 0;
+ compressed_lines = LE_16(&buf[stream_ptr]);
+ stream_ptr += 2;
+ while (compressed_lines > 0) {
+ line_packets = LE_16(&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;
+ 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 = buf[stream_ptr++];
+ if (byte_run < 0) {
+ byte_run = -byte_run;
+ pixel = LE_16(&buf[stream_ptr]);
+ stream_ptr += 2;
+ CHECK_PIXEL_PTR(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(byte_run);
+ for (j = 0; j < byte_run; j++,
pixel_countdown--) {
+ *((signed short*)(&pixels[pixel_ptr]))
= LE_16(&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 balck in both
15Bpp and 16Bpp modes. */
+ memset(pixels, 0x0000,
+ s->frame.linesize[0] * s->avctx->height * 2);
+ 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 = 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)\n",
+ pixel_countdown);
+ }
+ } 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)\n",
+ pixel_countdown);
+ }
+ }
+ }
+
+ /* Now FLX is strange, in that it is "byte" as opposed
to "pixel" run length compressed.
+ * This doesnt give us any good oportunity to perform
word endian conversion
+ * during decompression. So if its requried (ie, this
isnt a LE target, we do
+ * a second pass over the line here, swapping the bytes.
+ */
+ pixel = 0xFF00;
+ if (0xFF00 != LE_16(&pixel)) /* Check if its not an LE
Target */
+ {
+ pixel_ptr = y_ptr;
+ pixel_countdown = s->avctx->width;
+ while (pixel_countdown > 0) {
+ *((signed short*)(&pixels[pixel_ptr])) =
LE_16(&buf[pixel_ptr]);
+ pixel_ptr += 2;
+ }
+ }
+ 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 = buf[stream_ptr++];
+ if (byte_run > 0) {
+ pixel = LE_16(&buf[stream_ptr]);
+ stream_ptr += 2;
+ CHECK_PIXEL_PTR(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(byte_run);
+ for (j = 0; j < byte_run; j++) {
+ *((signed short*)(&pixels[pixel_ptr])) =
LE_16(&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_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])) =
LE_16(&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;
+ }
+
+ 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);
+
+
+ *data_size=sizeof(AVFrame);
+ *(AVFrame*)data = s->frame;
+
+ return buf_size;
+}
+
+static int flic_decode_frame_24BPP(AVCodecContext *avctx,
+ void *data, int *data_size,
+ uint8_t *buf, int buf_size)
+{
+ av_log(avctx, AV_LOG_ERROR, "24Bpp FLC Unsupported due to lack of
test files.\n");
+ return -1;
+}
+
+static int flic_decode_frame(AVCodecContext *avctx,
+ void *data, int *data_size,
+ uint8_t *buf, int buf_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);
+ }
+
+ /* Shouldnt 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 Format of FLC. My Science cant
explain how this happened\n");
+ return -1;
+}
+
+
static int flic_decode_end(AVCodecContext *avctx)
{
FlicDecodeContext *s = avctx->priv_data;
@@ -404,5 +737,8 @@
flic_decode_end,
flic_decode_frame,
CODEC_CAP_DR1,
+ NULL,
+ NULL,
+ NULL,
NULL
};
diff -Naur ffmpeg-cvs/libavformat/flic.c ffmpeg-edits/libavformat/flic.c
--- ffmpeg-cvs/libavformat/flic.c 2005-07-18 09:24:35.000000000 +1100
+++ ffmpeg-edits/libavformat/flic.c 2005-10-07 11:05:17.000000000 +1100
@@ -33,6 +33,8 @@
#define FLIC_FILE_MAGIC_1 0xAF11
#define FLIC_FILE_MAGIC_2 0xAF12
+#define FLIC_FILE_MAGIC_3 0xAF44 /* Flic Type for Extended FLX Format
which
+ originated in Dave's Targa
Animator (DTA) */
#define FLIC_CHUNK_MAGIC_1 0xF1FA
#define FLIC_CHUNK_MAGIC_2 0xF5FA
#define FLIC_MC_PTS_INC 6000 /* pts increment for Magic Carpet game
FLIs */
@@ -56,7 +58,8 @@
magic_number = LE_16(&p->buf[4]);
if ((magic_number != FLIC_FILE_MAGIC_1) &&
- (magic_number != FLIC_FILE_MAGIC_2))
+ (magic_number != FLIC_FILE_MAGIC_2) &&
+ (magic_number != FLIC_FILE_MAGIC_3))
return 0;
return AVPROBE_SCORE_MAX;
@@ -129,7 +132,8 @@
* therefore, the frame pts increment = n * 1285.7
*/
flic->frame_pts_inc = speed * 1285.7;
- } else if (magic_number == FLIC_FILE_MAGIC_2) {
+ } else if ((magic_number == FLIC_FILE_MAGIC_2) ||
+ (magic_number == FLIC_FILE_MAGIC_3)) {
/*
* in this case, the speed (n) is number of milliseconds
between frames:
*
@@ -206,7 +210,7 @@
static AVInputFormat flic_iformat = {
"flic",
- "FLI/FLC animation format",
+ "FLI/FLC/FLX animation format",
sizeof(FlicDemuxContext),
flic_probe,
flic_read_header,
More information about the ffmpeg-devel
mailing list