[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