[Ffmpeg-devel] TIFF LZW decoder (was: [PATCH] change gif demuxer to gif decoder)

Kostya kostya.shishkov
Wed Oct 25 15:07:56 CEST 2006


On Wed, Oct 25, 2006 at 01:11:15PM +0200, Michael Niedermayer wrote:
> Hi
> 
> On Wed, Oct 25, 2006 at 07:34:18AM +0300, Kostya wrote:
> > On Tue, Oct 24, 2006 at 12:51:02PM +0200, Michael Niedermayer wrote:
> > > Hi
> > > 
> > > On Tue, Oct 24, 2006 at 12:24:15PM +0300, Kostya wrote:
> > > > On Tue, Oct 24, 2006 at 07:35:01AM +0300, Kostya wrote:
> > > > > 
> > > > > As I understand, it may not need the wrapper, just two additional fields in
> > > > > structure: GetCode() pointer and dict_limit (when dictionary size reaches it,
> > > > > it should be expanded). I also tend to pass buffer and size to the structure
> > > > > instead of using bytestream.
> > > > > 
> > > > > I intend to present this scheme to the end of week.
> > > >  
> > > > Here it is. 
> > > 
> > > i think that the LZWState struct should not be in the "public"
> > > header for lzw, but rather in either lzw.c (or lzw_internal.h)
> > > a "typedef void LZWState;" in lzw.h should be enough ...
> > 
> > I think that would cause unneeded complexity (decoders should know
> > the size of LZWState anyway).
> 
> the problem with knowing the size is that code which uses lzw becomes 
> dependant on the specific size, for libavcodec it doesnt matter much, just
> 2-3 files more which need to be compiled if the size changes but if lzw would
> be used from outside libavcodec (or libavutil if we move it there) then
> we couldnt change LZWState in any way without breaking binary compatibility
> 
> iam not sure is there some interrest in having lzw in libavutil and useable
> from outside? if no then id accept the LZWState in lzw.h even though i dont
> think its good design, its not like 1 pointer dereference per compressed
> block would have a meassureable impact on speed or that 1 av_malloc in
> ff_lzw_decode_init() and a av_free() call at the end would be alot of
> complexity (all IMHO)

Changed as you suggested. Please note that allocation and initialization are
two different functions as TIFF requires LZW decoder to be reinited for each line
and placing alloc/free to codec_decode_init/end seems logical too.

As for moving it to lavutil - it can be done any time but LZW is tied to decoders
and I don't see any sense in doing that (except as a new feature of lavutil).

> and about complexity ....
> id suggest to replace the get_code function pointer by a simple
> get_code(...){
>     if(TIFF){
>     }else{
>     }
> }
> 
> not only is it simpler but it also should be faster as it can be
> inlined while a function pointed to by a function pointer can not (easily)
> which means copy arguments to the stack, read function pointer, call it,
> save clobbered registers, read arguments from the stack, ..., restore
> clobbered registers, return

Done

-------------- next part --------------
Index: libavcodec/lzw.c
===================================================================
--- libavcodec/lzw.c	(revision 0)
+++ libavcodec/lzw.c	(revision 0)
@@ -0,0 +1,262 @@
+/*
+ * LZW decoder
+ * Copyright (c) 2003 Fabrice Bellard.
+ * Copyright (c) 2006 Konstantin Shishkov.
+ *
+ * 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 lzw.c
+ * @brief LZW decoding routines
+ * @author Fabrice Bellard
+ * Modified for use in TIFF by Konstantin Shishkov
+ */
+
+#include "avcodec.h"
+#include "lzw.h"
+
+#define LZW_MAXBITS                 12
+#define LZW_SIZTABLE                (1<<LZW_MAXBITS)
+
+static const uint16_t mask[17] =
+{
+    0x0000, 0x0001, 0x0003, 0x0007,
+    0x000F, 0x001F, 0x003F, 0x007F,
+    0x00FF, 0x01FF, 0x03FF, 0x07FF,
+    0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
+};
+
+struct LZWState {
+    int eob_reached;
+    uint8_t *pbuf, *ebuf;
+    int bbits;
+    unsigned int bbuf;
+
+    int mode;                   ///< Decoder mode
+    int cursize;                ///< The current code size
+    int curmask;
+    int codesize;
+    int clear_code;
+    int end_code;
+    int newcodes;               ///< First available code
+    int top_slot;               ///< Highest code for current size
+    int top_slot2;              ///< Highest possible code for current size (<=top_slot)
+    int slot;                   ///< Last read code
+    int fc, oc;
+    uint8_t *sp;
+    uint8_t stack[LZW_SIZTABLE];
+    uint8_t suffix[LZW_SIZTABLE];
+    uint16_t prefix[LZW_SIZTABLE];
+    int bs;                     ///< current buffer size for GIF
+};
+
+/* get one code from stream */
+static int lzw_get_code(struct LZWState * s)
+{
+    int c, sizbuf;
+
+    if(s->mode == FF_LZW_GIF) {
+        while (s->bbits < s->cursize) {
+            if (!s->bs) {
+                sizbuf = *s->pbuf++;
+                s->bs = sizbuf;
+                if(!sizbuf) {
+                    s->eob_reached = 1;
+                }
+            }
+            s->bbuf |= (*s->pbuf++) << s->bbits;
+            s->bbits += 8;
+            s->bs--;
+        }
+        c = s->bbuf & s->curmask;
+       s->bbuf >>= s->cursize;
+    } else { // TIFF
+        while (s->bbits < s->cursize) {
+            if (s->pbuf >= s->ebuf) {
+                s->eob_reached = 1;
+            }
+            s->bbuf = (s->bbuf << 8) | (*s->pbuf++);
+            s->bbits += 8;
+        }
+        c = (s->bbuf >> (s->bbits - s->cursize)) & s->curmask;
+    }
+    s->bbits -= s->cursize;
+    return c;
+}
+
+uint8_t* ff_lzw_cur_ptr(LZWState *p)
+{
+    return ((struct LZWState*)p)->pbuf;
+}
+
+void ff_lzw_decode_tail(LZWState *p)
+{
+    struct LZWState *s = (struct LZWState *)p;
+    while(!s->eob_reached)
+        lzw_get_code(s);
+}
+
+void ff_lzw_decode_open(LZWState **p)
+{
+    *p = av_mallocz(sizeof(struct LZWState));
+}
+
+void ff_lzw_decode_close(LZWState **p)
+{
+    av_freep(p);
+}
+
+/**
+ * Initialize LZW decoder
+ * @param s LZW context
+ * @param csize initial code size in bits
+ * @param buf input data
+ * @param buf_size input data size
+ * @param mode decoder working mode - either GIF or TIFF
+ */
+int ff_lzw_decode_init(LZWState *p, int csize, uint8_t *buf, int buf_size, int mode)
+{
+    struct LZWState *s = (struct LZWState *)p;
+
+    if(csize < 1 || csize > LZW_MAXBITS)
+        return -1;
+    /* read buffer */
+    s->eob_reached = 0;
+    s->pbuf = buf;
+    s->ebuf = s->pbuf + buf_size;
+    s->bbuf = 0;
+    s->bbits = 0;
+    s->bs = 0;
+
+    /* decoder */
+    s->codesize = csize;
+    s->cursize = s->codesize + 1;
+    s->curmask = mask[s->cursize];
+    s->top_slot = 1 << s->cursize;
+    s->clear_code = 1 << s->codesize;
+    s->end_code = s->clear_code + 1;
+    s->slot = s->newcodes = s->clear_code + 2;
+    s->oc = s->fc = 0;
+    s->sp = s->stack;
+
+    s->mode = mode;
+    switch(s->mode){
+    case FF_LZW_GIF:
+        s->top_slot2 = s->top_slot;
+        break;
+    case FF_LZW_TIFF:
+        s->top_slot2 = s->top_slot - 1;
+        break;
+    default:
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * Decode given number of bytes
+ * NOTE: the algorithm here is inspired from the LZW GIF decoder
+ *  written by Steven A. Bennett in 1987.
+ *
+ * @param s LZW context
+ * @param buf output buffer
+ * @param len number of bytes to decode
+ * @return number of bytes decoded
+ */
+int ff_lzw_decode(LZWState *p, uint8_t *buf, int len){
+    int l, c, code, oc, fc;
+    uint8_t *sp;
+    struct LZWState *s = (struct LZWState *)p;
+
+    if (s->end_code < 0)
+        return 0;
+
+    l = len;
+    sp = s->sp;
+    oc = s->oc;
+    fc = s->fc;
+
+    while (sp > s->stack) {
+        *buf++ = *(--sp);
+        if ((--l) == 0)
+            goto the_end;
+    }
+
+    for (;;) {
+        c = lzw_get_code(s);
+        if (c == s->end_code) {
+            s->end_code = -1;
+            break;
+        } else if (c == s->clear_code) {
+            s->cursize = s->codesize + 1;
+            s->curmask = mask[s->cursize];
+            s->slot = s->newcodes;
+            s->top_slot = 1 << s->cursize;
+            s->top_slot2 = s->top_slot;
+            if(s->mode == FF_LZW_TIFF)
+                s->top_slot2--;
+            while ((c = lzw_get_code(s)) == s->clear_code);
+            if (c == s->end_code) {
+                s->end_code = -1;
+                break;
+            }
+            /* test error */
+            if (c >= s->slot)
+                c = 0;
+            fc = oc = c;
+            *buf++ = c;
+            if ((--l) == 0)
+                break;
+        } else {
+            code = c;
+            if (code >= s->slot) {
+                *sp++ = fc;
+                code = oc;
+            }
+            while (code >= s->newcodes) {
+                *sp++ = s->suffix[code];
+                code = s->prefix[code];
+            }
+            *sp++ = code;
+            if (s->slot < s->top_slot) {
+                s->suffix[s->slot] = fc = code;
+                s->prefix[s->slot++] = oc;
+                oc = c;
+            }
+            if (s->slot >= s->top_slot2) {
+                if (s->cursize < LZW_MAXBITS) {
+                    s->top_slot <<= 1;
+                    s->top_slot2 = s->top_slot;
+                    if(s->mode == FF_LZW_TIFF)
+                        s->top_slot2--;
+                    s->curmask = mask[++s->cursize];
+                }
+            }
+            while (sp > s->stack) {
+                *buf++ = *(--sp);
+                if ((--l) == 0)
+                    goto the_end;
+            }
+        }
+    }
+  the_end:
+    s->sp = sp;
+    s->oc = oc;
+    s->fc = fc;
+    return len - l;
+}
Index: libavcodec/lzw.h
===================================================================
--- libavcodec/lzw.h	(revision 0)
+++ libavcodec/lzw.h	(revision 0)
@@ -0,0 +1,49 @@
+/*
+ * LZW decoder
+ * Copyright (c) 2003 Fabrice Bellard.
+ * Copyright (c) 2006 Konstantin Shishkov.
+ *
+ * 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 lzw.h
+ * @brief LZW decoding routines
+ * @author Fabrice Bellard
+ * Modified for use in TIFF by Konstantin Shishkov
+ */
+
+#ifndef LZW_H
+#define LZW_H
+
+enum FF_LZW_MODES{
+    FF_LZW_GIF,
+    FF_LZW_TIFF
+};
+
+/* clients should not know what LZWState is */
+typedef void LZWState;
+
+/* first two functions de/allocate memory for LZWState */
+void ff_lzw_decode_open(LZWState **p);
+void ff_lzw_decode_close(LZWState **p);
+int ff_lzw_decode_init(LZWState *s, int csize, uint8_t *buf, int buf_size, int mode);
+int ff_lzw_decode(LZWState *s, uint8_t *buf, int len);
+uint8_t* ff_lzw_cur_ptr(LZWState *lzw);
+void ff_lzw_decode_tail(LZWState *lzw);
+
+#endif
Index: libavcodec/Makefile
===================================================================
--- libavcodec/Makefile	(revision 6781)
+++ libavcodec/Makefile	(working copy)
@@ -81,7 +81,7 @@
 OBJS-$(CONFIG_FLIC_DECODER)            += flicvideo.o
 OBJS-$(CONFIG_FOURXM_DECODER)          += 4xm.o
 OBJS-$(CONFIG_FRAPS_DECODER)           += fraps.o
-OBJS-$(CONFIG_GIF_DECODER)             += gifdec.o
+OBJS-$(CONFIG_GIF_DECODER)             += gifdec.o lzw.o
 OBJS-$(CONFIG_H261_DECODER)            += h261.o
 OBJS-$(CONFIG_H261_ENCODER)            += h261.o
 OBJS-$(CONFIG_H264_DECODER)            += h264.o
@@ -132,7 +132,7 @@
 OBJS-$(CONFIG_SVQ3_DECODER)            += h264.o
 OBJS-$(CONFIG_TARGA_DECODER)           += targa.o
 OBJS-$(CONFIG_THEORA_DECODER)          += vp3.o
-OBJS-$(CONFIG_TIFF_DECODER)            += tiff.o
+OBJS-$(CONFIG_TIFF_DECODER)            += tiff.o lzw.o
 OBJS-$(CONFIG_TRUEMOTION1_DECODER)     += truemotion1.o
 OBJS-$(CONFIG_TRUEMOTION2_DECODER)     += truemotion2.o
 OBJS-$(CONFIG_TRUESPEECH_DECODER)      += truespeech.o
Index: libavcodec/tiff.c
===================================================================
--- libavcodec/tiff.c	(revision 6781)
+++ libavcodec/tiff.c	(working copy)
@@ -23,6 +23,7 @@
 #ifdef CONFIG_ZLIB
 #include <zlib.h>
 #endif
+#include "lzw.h"
 
 /* abridged list of TIFF tags */
 enum TiffTags{
@@ -74,6 +75,7 @@
     uint8_t* stripdata;
     uint8_t* stripsizes;
     int stripsize, stripoff;
+    LZWState *lzw;
 } TiffContext;
 
 static int tget_short(uint8_t **p, int le){
@@ -126,6 +128,12 @@
         return 0;
     }
 #endif
+    if(s->compr == TIFF_LZW){
+        if(ff_lzw_decode_init(s->lzw, 8, src, size, FF_LZW_TIFF) < 0){
+            av_log(s->avctx, AV_LOG_ERROR, "Error initializing LZW decoder\n");
+            return -1;
+        }
+    }
     for(line = 0; line < lines; line++){
         if(src - ssrc > size){
             av_log(s->avctx, AV_LOG_ERROR, "Source data overread\n");
@@ -160,6 +168,13 @@
                 }
             }
             break;
+        case TIFF_LZW:
+            pixels = ff_lzw_decode(s->lzw, dst, width);
+            if(pixels < width){
+                av_log(s->avctx, AV_LOG_ERROR, "Decoded only %i bytes of %i\n", pixels, width);
+                return -1;
+            }
+            break;
         }
         dst += stride;
     }
@@ -247,6 +262,7 @@
         switch(s->compr){
         case TIFF_RAW:
         case TIFF_PACKBITS:
+        case TIFF_LZW:
             break;
         case TIFF_DEFLATE:
         case TIFF_ADOBE_DEFLATE:
@@ -256,9 +272,6 @@
             av_log(s->avctx, AV_LOG_ERROR, "Deflate: ZLib not compiled in\n");
             return -1;
 #endif
-        case TIFF_LZW:
-            av_log(s->avctx, AV_LOG_ERROR, "LZW: not implemented yet\n");
-            return -1;
         case TIFF_G3:
             av_log(s->avctx, AV_LOG_ERROR, "CCITT G3 compression is not supported\n");
             return -1;
@@ -405,6 +418,7 @@
     avcodec_get_frame_defaults((AVFrame*)&s->picture);
     avctx->coded_frame= (AVFrame*)&s->picture;
     s->picture.data[0] = NULL;
+    ff_lzw_decode_open(&s->lzw);
 
     return 0;
 }
@@ -413,6 +427,7 @@
 {
     TiffContext * const s = avctx->priv_data;
 
+    ff_lzw_decode_close(&s->lzw);
     if(s->picture.data[0])
         avctx->release_buffer(avctx, &s->picture);
     return 0;
Index: libavcodec/gifdec.c
===================================================================
--- libavcodec/gifdec.c	(revision 6781)
+++ libavcodec/gifdec.c	(working copy)
@@ -24,10 +24,8 @@
 
 #include "avcodec.h"
 #include "bytestream.h"
+#include "lzw.h"
 
-#define MAXBITS                 12
-#define SIZTABLE                (1<<MAXBITS)
-
 #define GCE_DISPOSAL_NONE       0
 #define GCE_DISPOSAL_INPLACE    1
 #define GCE_DISPOSAL_BACKGROUND 2
@@ -50,175 +48,16 @@
 
     /* LZW compatible decoder */
     uint8_t *bytestream;
-    int eob_reached;
-    uint8_t *pbuf, *ebuf;
-    int bbits;
-    unsigned int bbuf;
+    LZWState *lzw;
 
-    int cursize;                /* The current code size */
-    int curmask;
-    int codesize;
-    int clear_code;
-    int end_code;
-    int newcodes;               /* First available code */
-    int top_slot;               /* Highest code for current size */
-    int slot;                   /* Last read code */
-    int fc, oc;
-    uint8_t *sp;
-    uint8_t stack[SIZTABLE];
-    uint8_t suffix[SIZTABLE];
-    uint16_t prefix[SIZTABLE];
-
     /* aux buffers */
     uint8_t global_palette[256 * 3];
     uint8_t local_palette[256 * 3];
-    uint8_t buf[256];
 } GifState;
 
 static const uint8_t gif87a_sig[6] = "GIF87a";
 static const uint8_t gif89a_sig[6] = "GIF89a";
 
-static const uint16_t mask[17] =
-{
-    0x0000, 0x0001, 0x0003, 0x0007,
-    0x000F, 0x001F, 0x003F, 0x007F,
-    0x00FF, 0x01FF, 0x03FF, 0x07FF,
-    0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
-};
-
-static void GLZWDecodeInit(GifState * s, int csize)
-{
-    /* read buffer */
-    s->eob_reached = 0;
-    s->pbuf = s->buf;
-    s->ebuf = s->buf;
-    s->bbuf = 0;
-    s->bbits = 0;
-
-    /* decoder */
-    s->codesize = csize;
-    s->cursize = s->codesize + 1;
-    s->curmask = mask[s->cursize];
-    s->top_slot = 1 << s->cursize;
-    s->clear_code = 1 << s->codesize;
-    s->end_code = s->clear_code + 1;
-    s->slot = s->newcodes = s->clear_code + 2;
-    s->oc = s->fc = 0;
-    s->sp = s->stack;
-}
-
-/* XXX: optimize */
-static inline int GetCode(GifState * s)
-{
-    int c, sizbuf;
-    uint8_t *ptr;
-
-    while (s->bbits < s->cursize) {
-        ptr = s->pbuf;
-        if (ptr >= s->ebuf) {
-            if (!s->eob_reached) {
-                sizbuf = bytestream_get_byte(&s->bytestream);
-                s->ebuf = s->buf + sizbuf;
-                s->pbuf = s->buf;
-                if (sizbuf > 0) {
-                    bytestream_get_buffer(&s->bytestream, s->buf, sizbuf);
-                } else {
-                    s->eob_reached = 1;
-                }
-            }
-            ptr = s->pbuf;
-        }
-        s->bbuf |= ptr[0] << s->bbits;
-        ptr++;
-        s->pbuf = ptr;
-        s->bbits += 8;
-    }
-    c = s->bbuf & s->curmask;
-    s->bbuf >>= s->cursize;
-    s->bbits -= s->cursize;
-    return c;
-}
-
-/* NOTE: the algorithm here is inspired from the LZW GIF decoder
-   written by Steven A. Bennett in 1987. */
-/* return the number of byte decoded */
-static int GLZWDecode(GifState * s, uint8_t * buf, int len)
-{
-    int l, c, code, oc, fc;
-    uint8_t *sp;
-
-    if (s->end_code < 0)
-        return 0;
-
-    l = len;
-    sp = s->sp;
-    oc = s->oc;
-    fc = s->fc;
-
-    while (sp > s->stack) {
-        *buf++ = *(--sp);
-        if ((--l) == 0)
-            goto the_end;
-    }
-
-    for (;;) {
-        c = GetCode(s);
-        if (c == s->end_code) {
-            s->end_code = -1;
-            break;
-        } else if (c == s->clear_code) {
-            s->cursize = s->codesize + 1;
-            s->curmask = mask[s->cursize];
-            s->slot = s->newcodes;
-            s->top_slot = 1 << s->cursize;
-            while ((c = GetCode(s)) == s->clear_code);
-            if (c == s->end_code) {
-                s->end_code = -1;
-                break;
-            }
-            /* test error */
-            if (c >= s->slot)
-                c = 0;
-            fc = oc = c;
-            *buf++ = c;
-            if ((--l) == 0)
-                break;
-        } else {
-            code = c;
-            if (code >= s->slot) {
-                *sp++ = fc;
-                code = oc;
-            }
-            while (code >= s->newcodes) {
-                *sp++ = s->suffix[code];
-                code = s->prefix[code];
-            }
-            *sp++ = code;
-            if (s->slot < s->top_slot) {
-                s->suffix[s->slot] = fc = code;
-                s->prefix[s->slot++] = oc;
-                oc = c;
-            }
-            if (s->slot >= s->top_slot) {
-                if (s->cursize < MAXBITS) {
-                    s->top_slot <<= 1;
-                    s->curmask = mask[++s->cursize];
-                }
-            }
-            while (sp > s->stack) {
-                *buf++ = *(--sp);
-                if ((--l) == 0)
-                    goto the_end;
-            }
-        }
-    }
-  the_end:
-    s->sp = sp;
-    s->oc = oc;
-    s->fc = fc;
-    return len - l;
-}
-
 static int gif_read_image(GifState *s)
 {
     int left, top, width, height, bits_per_pixel, code_size, flags;
@@ -267,7 +106,8 @@
 
     /* now get the image data */
     code_size = bytestream_get_byte(&s->bytestream);
-    GLZWDecodeInit(s, code_size);
+    //TODO: add proper data size
+    ff_lzw_decode_init(s->lzw, code_size, s->bytestream, 0, FF_LZW_GIF);
 
     /* read all the image */
     linesize = s->picture.linesize[0];
@@ -276,7 +116,7 @@
     pass = 0;
     y1 = 0;
     for (y = 0; y < height; y++) {
-        GLZWDecode(s, ptr, width);
+        ff_lzw_decode(s->lzw, ptr, width);
         if (is_interleaved) {
             switch(pass) {
             default:
@@ -314,8 +154,8 @@
     av_free(line);
 
     /* read the garbage data until end marker is found */
-    while (!s->eob_reached)
-        GetCode(s);
+    ff_lzw_decode_tail(s->lzw);
+    s->bytestream = ff_lzw_cur_ptr(s->lzw);
     return 0;
 }
 
@@ -445,6 +285,7 @@
     avcodec_get_frame_defaults(&s->picture);
     avctx->coded_frame= &s->picture;
     s->picture.data[0] = NULL;
+    ff_lzw_decode_open(&s->lzw);
     return 0;
 }
 
@@ -483,6 +324,7 @@
 {
     GifState *s = avctx->priv_data;
 
+    ff_lzw_decode_close(&s->lzw);
     if(s->picture.data[0])
         avctx->release_buffer(avctx, &s->picture);
     return 0;



More information about the ffmpeg-devel mailing list