[FFmpeg-devel] [PATCH V3] [RFC] GSoC: FLIF16 Image format parser

Anamitra Ghorui aghorui at teknik.io
Fri Mar 6 20:10:04 EET 2020


The parser has been tested and is able to correctly identify the start 
of the compressed bitstream. The patch has a set of printf statements 
which print a "tracing table" of the behaviour. Upon Nicolas George's 
suggestion I have made it so that the varints are read into a uint64_t.
Hence the varints are limited in the range 0 to 2^64 - 1.
The test cases are as follows:
1. 1x1 png image (a.flif)
2. 1x1 png image with dummy EXIF data (a1.flif)
3. 2x2 png image (b.flif)
4. 300x200 png image (d.flif)
5. 10x10 gif image, 2 frames (f.flif)
These have been provided as an attachment.
The way I have used AVERROR in the parser may be wrong.

The testing code has been adapted from:
https://ffmpeg.org/doxygen/trunk/decode_video_8c-example.html
The concerned part is (available as attachment):
...
	while (!feof(f)) {
        /* read raw data from the input file */
        data_size = fread(inbuf, 1, INBUF_SIZE, f);
        if (!data_size)
            break;
        /* use the parser to split the data into frames */
        data = inbuf;
        while (data_size > 0) {
            ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
                                   data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if (ret < 0) {
                fprintf(stderr, "Error while parsing\n");
                exit(1);
            }
            data      += ret;
            data_size -= ret;
            if (pkt->size > 0)
            {
                printf("Reached bitstream at 0x%x (%dd)\n", pkt->size, 
                       pkt->size);
                goto end;
            }
        }
    }
    end:
...

Now comes the part of decompressing the bitstream and finding the bounds
of the data. I haven't yet properly gone through the reference
implementation due to a lack of time (I will have now), but I'm thinking
of defining a few functions:

int ff_flif16_read_rac(uint8_t *buf, unsigned int buf_size,
					unsinged int offset,
					FLIF16ChanceTable chance);
int ff_flif16_read_uni_int(int min, int max);
int ff_flif16_read_nz_int(int min, int max,  FLIF16ChanceTable context);
int ff_flif16_read_gnz_int(int min, int max, FLIF16ChanceTable context);
(...)

The decoder itself will not handle decompressing or decoding the
bitstream, rather we will alter the buffer to add in the decompressed
bitstream, then run it through the parser and add it to the AVPacket,
and finally pass it to the decoder. The decoder will then decode all
the frames from that single packet.

---
 Changelog                  |   1 +
 configure                  |   2 +
 libavcodec/Makefile        |   3 +
 libavcodec/allcodecs.c     |   2 +
 libavcodec/avcodec.h       |   1 +
 libavcodec/codec_desc.c    |   7 ++
 libavcodec/flif16.h        |  44 +++++++++
 libavcodec/flif16_parser.c | 193 +++++++++++++++++++++++++++++++++++++
 libavcodec/flif16dec.c     |  22 +++++
 libavcodec/flif16enc.c     |  22 +++++
 libavcodec/parsers.c       |   1 +
 libavformat/img2.c         |   1 +
 12 files changed, 299 insertions(+)
 create mode 100644 libavcodec/flif16.h
 create mode 100644 libavcodec/flif16_parser.c
 create mode 100644 libavcodec/flif16dec.c
 create mode 100644 libavcodec/flif16enc.c

diff --git a/Changelog b/Changelog
index cb310a3abc..4f88dbaadb 100644
--- a/Changelog
+++ b/Changelog
@@ -43,6 +43,7 @@ version <next>:
 - Rayman 2 ADPCM decoder
 - Rayman 2 APM demuxer
 - cas video filter
+- FLIF16 decoder
 
 
 version 4.2:
diff --git a/configure b/configure
index 06e3a7b2a8..872ee750e0 100755
--- a/configure
+++ b/configure
@@ -2709,6 +2709,8 @@ ffvhuff_encoder_select="huffyuv_encoder"
 fic_decoder_select="golomb"
 flac_decoder_select="flacdsp"
 flac_encoder_select="bswapdsp flacdsp lpc"
+flif16_decoder_select="flif16dec"
+flif16_encoder_select="flif16enc"
 flashsv2_decoder_deps="zlib"
 flashsv2_encoder_deps="zlib"
 flashsv_decoder_deps="zlib"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index f1c032b456..b78b2cc550 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -319,6 +319,8 @@ OBJS-$(CONFIG_FLASHSV_ENCODER)         += flashsvenc.o
 OBJS-$(CONFIG_FLASHSV2_ENCODER)        += flashsv2enc.o
 OBJS-$(CONFIG_FLASHSV2_DECODER)        += flashsv.o
 OBJS-$(CONFIG_FLIC_DECODER)            += flicvideo.o
+OBJS-$(CONFIG_FLIF16_DECODER)          += flif16dec.o
+OBJS-$(CONFIG_FLIF16_ENCODER)          += flif16enc.o
 OBJS-$(CONFIG_FMVC_DECODER)            += fmvc.o
 OBJS-$(CONFIG_FOURXM_DECODER)          += 4xm.o
 OBJS-$(CONFIG_FRAPS_DECODER)           += fraps.o
@@ -1046,6 +1048,7 @@ OBJS-$(CONFIG_DVD_NAV_PARSER)          += dvd_nav_parser.o
 OBJS-$(CONFIG_DVDSUB_PARSER)           += dvdsub_parser.o
 OBJS-$(CONFIG_FLAC_PARSER)             += flac_parser.o flacdata.o flac.o \
                                           vorbis_data.o
+OBJS-$(CONFIG_FLIF16_PARSER)           += flif16_parser.o
 OBJS-$(CONFIG_G723_1_PARSER)           += g723_1_parser.o
 OBJS-$(CONFIG_G729_PARSER)             += g729_parser.o
 OBJS-$(CONFIG_GIF_PARSER)              += gif_parser.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 674995df72..79a2d89ee8 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -119,6 +119,8 @@ extern AVCodec ff_flashsv_decoder;
 extern AVCodec ff_flashsv2_encoder;
 extern AVCodec ff_flashsv2_decoder;
 extern AVCodec ff_flic_decoder;
+extern AVCodec ff_flif16_decoder;
+extern AVCodec ff_flif16_encoder;
 extern AVCodec ff_flv_encoder;
 extern AVCodec ff_flv_decoder;
 extern AVCodec ff_fmvc_decoder;
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 894a9e5565..4e2af45dd0 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -461,6 +461,7 @@ enum AVCodecID {
     AV_CODEC_ID_MVDV,
     AV_CODEC_ID_MVHA,
     AV_CODEC_ID_CDTOONS,
+    AV_CODEC_ID_FLIF16,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 52178e7afe..993ec450c1 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1754,6 +1754,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("CDToons video"),
         .props     = AV_CODEC_PROP_LOSSLESS,
     },
+    {
+        .id        = AV_CODEC_ID_FLIF16,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "flif16",
+        .long_name = NULL_IF_CONFIG_SMALL("FLIF16 (Free Lossless Image Format)"),
+        .props     = AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/flif16.h b/libavcodec/flif16.h
new file mode 100644
index 0000000000..ffb3853df9
--- /dev/null
+++ b/libavcodec/flif16.h
@@ -0,0 +1,44 @@
+/*
+ * FLIF16 Image Format Definitions
+ * Copyright (c) 2020 Anamitra Ghorui
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * FLIF16 format definitions and functions.
+ */
+
+#ifndef AVCODEC_FLIF16_H
+#define AVCODEC_FLIF16_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+static const uint8_t flif16_header[4] = "FLIF";
+
+/* WIP
+void flif16_read_rac(b, chance);
+void flif16_read_uni_int();
+void flif16_read_nz_int();
+void flif16_read_gnz_int();
+*/
+
+#define FF_FLIF16_VARINT_APPEND(a,x) a = (a << 7) | (uint64_t) (x & 127)
+
+#endif /* AVCODEC_FLIF16_H */
diff --git a/libavcodec/flif16_parser.c b/libavcodec/flif16_parser.c
new file mode 100644
index 0000000000..6f7f557e3e
--- /dev/null
+++ b/libavcodec/flif16_parser.c
@@ -0,0 +1,193 @@
+/*
+ * FLIF16 parser
+ * Copyright (c) 2020 Anamitra Ghorui
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ /**
+  * @file
+  * FLIF16 parser
+  */
+
+#include "flif16.h"
+#include "parser.h"
+#include "libavutil/avassert.h"
+#include "libavutil/bswap.h"
+
+#include <stdio.h> //remove
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef enum FLIF16ParseStates {
+    FLIF16_HEADER = 1,
+    FLIF16_METADATA,
+    FLIF16_BITSTREAM,
+    // FLIF16_TRANSFORM,
+    FLIF16_PIXELDATA,
+    FLIF16_CHECKSUM,
+    FLIF16_VARINT
+} FLIF16ParseStates;
+
+typedef struct FLIF16ParseContext {
+    ParseContext pc;
+    int state;              ///< The section of the file the parser is in currently.
+    unsigned int index;     ///< An index based on the current state. 
+    uint8_t iac;            ///< Interlaced, animated, color palette info
+    uint8_t varint;         ///< Number of varints to process in sequence
+    uint64_t width;
+    uint64_t height;
+    uint64_t frames;
+    uint64_t meta;          ///< Size of a meta chunk
+    uint64_t count;
+} FLIF16ParseContext;
+
+static int flif16_find_frame(FLIF16ParseContext *f, const uint8_t *buf,
+                             int buf_size)
+{
+    int next = END_NOT_FOUND;
+    int index;
+    
+    printf("pos\tfindex\tstate\tval\tw\th\tframes\tmeta\tvarintn\n");
+    for (index = 0; index < buf_size; index++) {
+        printf("%d\t%d\t%d\t0x%x\t0x%lx\t0x%lx\t%lx\t%lx\t%d\n", index, 
+               f->index, f->state, buf[index], f->width, f->height, f->frames,
+               f->meta, f->varint);
+        if (!f->state) {
+            if (!memcmp(flif16_header, buf + index, 4))
+                f->state = FLIF16_HEADER;
+            ++f->index;
+        } else if (f->state == FLIF16_HEADER) {
+            if (f->index == 3 + 1) { // Interlaced/animated/color palette info
+                f->iac = buf[index];
+            } else if (f->index == (3 + 1 + 1)) { // Start - 1 of the first varint
+                f->varint = 1;
+            } else if (f->varint) {
+                if (f->count == 9)
+                        return AVERROR(ENOMEM);
+
+                switch (f->varint) {
+                        case 1:
+                            FF_FLIF16_VARINT_APPEND(f->width, buf[index]);
+                            break;
+                        
+                        case 2:
+                            FF_FLIF16_VARINT_APPEND(f->height, buf[index]);
+                            break;
+                        
+                        case 3:
+                            FF_FLIF16_VARINT_APPEND(f->frames, buf[index]);
+                            break;
+                }
+                if (buf[index] < 128) {
+                    if (f->varint < (2 + (((f->iac >> 4) > 4)?1:0))) {
+                        switch (f->varint) {
+                            case 1: f->width++;  break;
+                            case 2: f->height++; break;
+                        }
+                        f->varint++;
+                        f->count = 0;
+                    } else {
+                        if (f->varint == 2)
+                            f->height++;
+                        if ((f->iac >> 4) > 4)
+                            f->frames += 2;
+                        else
+                            f->frames = 1;
+                        f->state = FLIF16_METADATA;
+                        f->varint = 0;
+                        f->index = 0;
+                        f->count = 0;
+                        continue;
+                    }
+                } else {
+                    f->count++;
+                }
+            }
+            f->index++;
+        } else if (f->state == FLIF16_METADATA) {
+            if (f->index == 0) {
+                // Identifier for the bitstream chunk is a null byte.
+                if (buf[index] == 0)
+                    f->state = FLIF16_BITSTREAM;
+            } else if (f->index < 3) {
+                // nop
+            } else if (f->index == 3) {
+                // Handle the size varint
+                f->varint = 1;
+            } else if (f->varint) {
+                if (f->count == 9)
+                    return AVERROR(ENOMEM);
+                if (buf[index] < 128) {
+                    f->varint = 0;
+                    f->count = 0;
+                }
+                FF_FLIF16_VARINT_APPEND(f->meta, buf[index]);
+                f->count++;
+            } else if (f->meta > 1) {
+                // increment varint until equal to size
+                f->meta--;
+            } else {
+                f->meta = 0;
+                f->index = 0;
+                continue;
+            }
+            f->index++;
+        } else if (f->state == FLIF16_BITSTREAM) {
+            /* The real stuff starts here. */
+            return index; // Currently here for testing purposes.
+        } else if (f->state == FLIF16_PIXELDATA) {
+            /* 
+             * Total uncompressed size will be equal to:
+             * nchannels * nframes * nwidth * nheight
+             */
+            return index; // Currently here for testing purposes.
+        }
+    }
+    printf("End not found\n");
+    return next;
+}
+
+static int flif16_parse(AVCodecParserContext *s, AVCodecContext *avctx,
+                        const uint8_t **poutbuf, int *poutbuf_size,
+                        const uint8_t *buf, int buf_size)
+{
+    FLIF16ParseContext *fpc = s->priv_data;
+    int next;
+    
+    next = flif16_find_frame(fpc, buf, buf_size);
+    
+    if (ff_combine_frame(&fpc->pc, next, &buf, &buf_size) < 0) {
+        *poutbuf      = NULL;
+        *poutbuf_size = 0;
+        return buf_size;
+    }
+    printf("Width:%lu\nHeight:%lu\nFrames:%lu\nEnd:%d\n", 
+           fpc->width, fpc->height, fpc->frames, buf_size);
+
+    *poutbuf      = buf;
+    *poutbuf_size = buf_size;
+    return next;
+}
+
+AVCodecParser ff_flif16_parser = {
+    .codec_ids      = { AV_CODEC_ID_FLIF16 },
+    .priv_data_size = sizeof(FLIF16ParseContext),
+    .parser_parse   = flif16_parse,
+    .parser_close   = ff_parse_close,
+};
+
diff --git a/libavcodec/flif16dec.c b/libavcodec/flif16dec.c
new file mode 100644
index 0000000000..afa8ffa756
--- /dev/null
+++ b/libavcodec/flif16dec.c
@@ -0,0 +1,22 @@
+#include "flif16.h"
+#include "avcodec.h"
+
+// TODO Add all Functions
+static int flif16_decode_frame(AVCodecContext *avctx,
+                               void *data, int *got_frame,
+                               AVPacket *avpkt)
+{
+    return -1;
+}
+
+AVCodec ff_flif16_decoder = {
+    .name           = "flif16",
+    .long_name      = NULL_IF_CONFIG_SMALL("FLIF (Free Lossless Image Format)"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_FLIF16,
+    .priv_data_size = 0,
+    .decode         = flif16_decode_frame,
+    //.capabilities   = 0,
+    //.caps_internal  = 0,
+    .priv_class     = NULL,
+};
diff --git a/libavcodec/flif16enc.c b/libavcodec/flif16enc.c
new file mode 100644
index 0000000000..5701ef23dc
--- /dev/null
+++ b/libavcodec/flif16enc.c
@@ -0,0 +1,22 @@
+#include "flif16.h"
+#include "avcodec.h"
+
+// TODO Add all Functions
+static int flif16_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
+                            const AVFrame *pict, int *got_packet)
+{
+    return -1;
+}
+
+AVCodec ff_flif16_encoder = {
+    .name           = "flif16",
+    .long_name      = NULL_IF_CONFIG_SMALL("FLIF (Free Lossless Image Format)"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_FLIF16,
+    .priv_data_size = 0,
+    //.init           = NULL,
+    .encode2        = flif16_encode_frame,
+    //.close          = NULL,
+    //.pix_fmts       = (const enum AVPixelFormat[]){},
+    .priv_class     = NULL,
+};
diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c
index 33a71de8a0..8b6eb954b3 100644
--- a/libavcodec/parsers.c
+++ b/libavcodec/parsers.c
@@ -40,6 +40,7 @@ extern AVCodecParser ff_dvbsub_parser;
 extern AVCodecParser ff_dvdsub_parser;
 extern AVCodecParser ff_dvd_nav_parser;
 extern AVCodecParser ff_flac_parser;
+extern AVCodecParser ff_flif16_parser;
 extern AVCodecParser ff_g723_1_parser;
 extern AVCodecParser ff_g729_parser;
 extern AVCodecParser ff_gif_parser;
diff --git a/libavformat/img2.c b/libavformat/img2.c
index 16bc9d2abd..14c11d0c82 100644
--- a/libavformat/img2.c
+++ b/libavformat/img2.c
@@ -81,6 +81,7 @@ const IdStrMap ff_img_tags[] = {
     { AV_CODEC_ID_XPM,        "xpm"      },
     { AV_CODEC_ID_XFACE,      "xface"    },
     { AV_CODEC_ID_XWD,        "xwd"      },
+    { AV_CODEC_ID_FLIF16,     "flif16"   },
     { AV_CODEC_ID_NONE,       NULL       }
 };
 
-- 
2.17.1
-------------- next part --------------
A non-text attachment was scrubbed...
Name: flif.zip
Type: application/zip
Size: 2595 bytes
Desc: not available
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20200306/f9da0976/attachment.zip>


More information about the ffmpeg-devel mailing list