diff mbox series

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

Message ID 6a72537f50fe24f0913b70b94a751399@teknik.io
State New
Headers show
Series [FFmpeg-devel,V3,RFC] GSoC: FLIF16 Image format parser | expand

Checks

Context Check Description
andriy/ffmpeg-patchwork fail Make fate failed

Commit Message

Anamitra Ghorui March 6, 2020, 6:10 p.m. UTC
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

Comments

Anamitra Ghorui March 7, 2020, 2:33 a.m. UTC | #1
I just realised that there is a range coder implementation 
already present in libavcodec. I'll look into it.
Jai Luthra March 14, 2020, 8:45 p.m. UTC | #2
Hi Anamitra,

Good progres on the parser, I tested it on the samples you provided, lgtm. 
Comments inline.

On Fri, Mar 06, 2020 at 06:10:04PM +0000, Anamitra Ghorui wrote:
>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:
>...
>

Yes afaics parser does not generally return errors. Usually the parser splits 
the frames out if it is able to, and the decoder verifies if something is 
wrong within the packet bitstream.

I am not certain how this should be handled here as you plan to do entropy 
decoding within the parser.

Parser's ret value being < 0 usually implies that frame start was in previous 
packet, but look around other parsers and ask in #ffmpeg-devel if your 
approach is feasible.

>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);
>(...)

I haven't heard from the other applicant who was working on the entropy 
decoding task, so I would suggest you can go ahead with implementing this. I 
think you may be able to use/modify the existing range coder ffmpeg module.

>
>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.
>


>+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

For testing this is good enough, but I think it would be a better idea to have 
separate fields in the context for interlaced/animated/color pallete etc. so 
that you don't need to do bitshifts everywhere you need to use them as done 
below.

>+    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)
Anamitra Ghorui March 15, 2020, 4:49 a.m. UTC | #3
Hello,

March 15, 2020 2:15 AM, "Jai Luthra" <me@jailuthra.in> wrote:
>On Fri, Mar 06, 2020 at 06:10:04PM +0000, Anamitra Ghorui wrote:
>>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.

>Yes afaics parser does not generally return errors. Usually the parser splits 
>the frames out if it is able to, and the decoder verifies if something is 
>wrong within the packet bitstream.
>
>I am not certain how this should be handled here as you plan to do entropy 
>decoding within the parser.
>
>Parser's ret value being < 0 usually implies that frame start was in previous 
>packet, but look around other parsers and ask in #ffmpeg-devel if your 
>approach is feasible.

The main reason I was trying to decode the bitstream in the parser itself was
that the specification doesn't mention anything about the size of the compressed
bitstream from what I can see. We however can read through the second/image 
description header and the uncompressed length of the pixel data is already 
known. 

Another approach I would suggest is simply to send the bytes over to the output 
buffer unaltered without doing anything, but then there is no need
to even really have the parser aside from just being congruent to the standard 
decoding procedure given in the example code (decode_video.c), and finding 
the start of the bitstream.

The decoder will then throw a series of AVERROR(EAGAIN)s while the bitstream is
being read.

I will ask around.

>>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);
>>(...)
>
>I haven't heard from the other applicant who was working on the entropy 
>decoding task, so I would suggest you can go ahead with implementing this. I 
>think you may be able to use/modify the existing range coder ffmpeg module.

I see, but I am a bit confused about the tasks that we are to complete during 
the official work period and now. Where should I stop?

>>
>>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.
>>
>
>
>>+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
>
>For testing this is good enough, but I think it would be a better idea to have 
>separate fields in the context for interlaced/animated/color pallete etc. so 
>that you don't need to do bitshifts everywhere you need to use them as done 
>below.

Okay

Regards,
Anamitra
Anamitra Ghorui March 15, 2020, 12:40 p.m. UTC | #4
Hello,

I am dealing dealing with a video (gif-like) file format in which there 
are compressed (entropy coded) segments of unknown length. However, the length 
of the uncompressed segment of the file is already known. Please check the 
previous mail by thread if context is required. There are no markers that 
indicate the end of these segments, and these segments are present at the end 
of the file.

One additional problem is that the frames of the video are interleaved, which
means that the parser will have to be provided with the whole pixel data/frame
data at once, and cannot be broken into individual frames.

I have been trying to figure out how to do go about parsing the file, and I
have come up with the following approaches:
1. Decompress the compressed segment in the parser itself, and supply that in
   the output buffer.

2. Keep providing the input buffer of arbitrary length as a packet to the decoder.
   The decoder will decompress the stream and process it accordingly. The decoder 
   will keep an internal buffer. It will return AVERROR(EAGAIN) until the full 
   compressed bitstream has been decoded and AVFrames can be generated.
   
   In this case the parser will not do much aside from finding the start of the
   file stream and returning the buffer.

I think approach 2 will be better in this case because error handling cannot be
done well in the parser. What do you suggest?
Anton Khirnov March 16, 2020, 9:55 a.m. UTC | #5
Quoting Anamitra Ghorui (2020-03-15 13:40:58)
> Hello,
> 
> I am dealing dealing with a video (gif-like) file format in which there 
> are compressed (entropy coded) segments of unknown length. However, the length 
> of the uncompressed segment of the file is already known. Please check the 
> previous mail by thread if context is required. There are no markers that 
> indicate the end of these segments, and these segments are present at the end 
> of the file.
> 
> One additional problem is that the frames of the video are interleaved, which
> means that the parser will have to be provided with the whole pixel data/frame
> data at once, and cannot be broken into individual frames.

What do you mean by "interleaved" exactly?

> 
> I have been trying to figure out how to do go about parsing the file, and I
> have come up with the following approaches:
> 1. Decompress the compressed segment in the parser itself, and supply that in
>    the output buffer.
> 
> 2. Keep providing the input buffer of arbitrary length as a packet to the decoder.
>    The decoder will decompress the stream and process it accordingly. The decoder 
>    will keep an internal buffer. It will return AVERROR(EAGAIN) until the full 
>    compressed bitstream has been decoded and AVFrames can be generated.
>    
>    In this case the parser will not do much aside from finding the start of the
>    file stream and returning the buffer.
> 
> I think approach 2 will be better in this case because error handling cannot be
> done well in the parser. What do you suggest?

Yes, 2. sounds saner than implementing a decoder inside a parser. The
new decoding API allows for arbitrary M:N mapping of packets to frames,
so this should not be a problem.
Anamitra Ghorui March 16, 2020, 12:31 p.m. UTC | #6
March 16, 2020 3:25 PM, "Anton Khirnov" <anton@khirnov.net> wrote:

> Quoting Anamitra Ghorui (2020-03-15 13:40:58)
> 
>> Hello,
>> 
>> I am dealing dealing with a video (gif-like) file format in which there
>> are compressed (entropy coded) segments of unknown length. However, the length
>> of the uncompressed segment of the file is already known. Please check the
>> previous mail by thread if context is required. There are no markers that
>> indicate the end of these segments, and these segments are present at the end
>> of the file.
>> 
>> One additional problem is that the frames of the video are interleaved, which
>> means that the parser will have to be provided with the whole pixel data/frame
>> data at once, and cannot be broken into individual frames.
> 
> What do you mean by "interleaved" exactly?

By interleaved I meant that the frames are not stored one after the another.
Instead, the nth row of pixels of all the frames are stored together. All
of these pixel row blocks (from row 0 to height) are stored together for 
each colour channel. Please refer to the diagram here for better 
visualisation if required:
https://ffmpeg.org/pipermail/ffmpeg-devel/2020-February/257797.html
Anton Khirnov March 16, 2020, 2:05 p.m. UTC | #7
Quoting Anamitra Ghorui (2020-03-16 13:31:53)
> March 16, 2020 3:25 PM, "Anton Khirnov" <anton@khirnov.net> wrote:
> 
> > Quoting Anamitra Ghorui (2020-03-15 13:40:58)
> > 
> >> Hello,
> >> 
> >> I am dealing dealing with a video (gif-like) file format in which there
> >> are compressed (entropy coded) segments of unknown length. However, the length
> >> of the uncompressed segment of the file is already known. Please check the
> >> previous mail by thread if context is required. There are no markers that
> >> indicate the end of these segments, and these segments are present at the end
> >> of the file.
> >> 
> >> One additional problem is that the frames of the video are interleaved, which
> >> means that the parser will have to be provided with the whole pixel data/frame
> >> data at once, and cannot be broken into individual frames.
> > 
> > What do you mean by "interleaved" exactly?
> 
> By interleaved I meant that the frames are not stored one after the another.
> Instead, the nth row of pixels of all the frames are stored together. All
> of these pixel row blocks (from row 0 to height) are stored together for 
> each colour channel. Please refer to the diagram here for better 
> visualisation if required:
> https://ffmpeg.org/pipermail/ffmpeg-devel/2020-February/257797.html

Impressively overcomplicated.

So yeah, either the decoder takes in the entire bitstream and then spits
out frames one by one or the demuxer does the decoding and exports
rawvideo data. The former is most likely better.
Nicolas George March 16, 2020, 2:10 p.m. UTC | #8
Anton Khirnov (12020-03-16):
> Impressively overcomplicated.

I suppose it is meant to enhance the compression ratio, with the
assumption that images change less in the time direction than in the y
direction.

> So yeah, either the decoder takes in the entire bitstream and then spits
> out frames one by one or the demuxer does the decoding and exports
> rawvideo data. The former is most likely better.

The demuxer would have to decode all frames and buffer them, that would
not bring any benefit. So I agree, the decode will eat the whole
bitstream and output all frames at once, it's the best solution.

Regards,
diff mbox series

Patch

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       }
 };