[FFmpeg-devel] [PATCH 2/2] zlib fallbacks for pngdec and zmbv.

Reimar Döffinger Reimar.Doeffinger at gmx.de
Thu Mar 24 16:33:30 CET 2016


This should demonstrate how well or badly the
minimal inflate fits into code designed around zlib.
I am not clear on whether the pngdec implementation
was done explicitly to save memory, some other reason,
or possibly for no good reason at all.

Signed-off-by: Reimar Döffinger <Reimar.Doeffinger at gmx.de>
---
 configure           |   3 --
 libavcodec/Makefile |   6 +--
 libavcodec/pngdec.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++-----
 libavcodec/zmbv.c   |  47 ++++++++++++++++-
 4 files changed, 184 insertions(+), 21 deletions(-)

diff --git a/configure b/configure
index 1b189328..9ea7d88 100755
--- a/configure
+++ b/configure
@@ -2290,7 +2290,6 @@ amrwb_decoder_select="lsp"
 amv_decoder_select="sp5x_decoder exif"
 amv_encoder_select="aandcttables jpegtables mpegvideoenc"
 ape_decoder_select="bswapdsp llauddsp"
-apng_decoder_select="zlib"
 apng_encoder_select="huffyuvencdsp zlib"
 asv1_decoder_select="blockdsp bswapdsp idctdsp"
 asv1_encoder_select="bswapdsp fdctdsp pixblockdsp"
@@ -2424,7 +2423,6 @@ nuv_decoder_select="idctdsp lzo"
 on2avc_decoder_select="mdct"
 opus_decoder_deps="swresample"
 opus_decoder_select="imdct15"
-png_decoder_select="zlib"
 png_encoder_select="huffyuvencdsp zlib"
 prores_decoder_select="blockdsp idctdsp"
 prores_encoder_select="fdctdsp"
@@ -2503,7 +2501,6 @@ xma2_decoder_select="wmapro_decoder"
 zerocodec_decoder_select="zlib"
 zlib_decoder_select="zlib"
 zlib_encoder_select="zlib"
-zmbv_decoder_select="zlib"
 zmbv_encoder_select="zlib"
 
 # hardware accelerators
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 6bb1af1..71e7087 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -175,7 +175,7 @@ OBJS-$(CONFIG_AMV_ENCODER)             += mjpegenc.o mjpegenc_common.o \
 OBJS-$(CONFIG_ANM_DECODER)             += anm.o
 OBJS-$(CONFIG_ANSI_DECODER)            += ansi.o cga_data.o
 OBJS-$(CONFIG_APE_DECODER)             += apedec.o
-OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o
+OBJS-$(CONFIG_APNG_DECODER)            += png.o pngdec.o pngdsp.o inflate.o
 OBJS-$(CONFIG_APNG_ENCODER)            += png.o pngenc.o
 OBJS-$(CONFIG_SSA_DECODER)             += assdec.o ass.o
 OBJS-$(CONFIG_SSA_ENCODER)             += assenc.o ass.o
@@ -427,7 +427,7 @@ OBJS-$(CONFIG_PGMYUV_ENCODER)          += pnmenc.o
 OBJS-$(CONFIG_PGSSUB_DECODER)          += pgssubdec.o
 OBJS-$(CONFIG_PICTOR_DECODER)          += pictordec.o cga_data.o
 OBJS-$(CONFIG_PJS_DECODER)             += textdec.o ass.o
-OBJS-$(CONFIG_PNG_DECODER)             += png.o pngdec.o pngdsp.o
+OBJS-$(CONFIG_PNG_DECODER)             += png.o pngdec.o pngdsp.o inflate.o
 OBJS-$(CONFIG_PNG_ENCODER)             += png.o pngenc.o
 OBJS-$(CONFIG_PPM_DECODER)             += pnmdec.o pnm.o
 OBJS-$(CONFIG_PPM_ENCODER)             += pnmenc.o
@@ -614,7 +614,7 @@ OBJS-$(CONFIG_YUV4_ENCODER)            += yuv4enc.o
 OBJS-$(CONFIG_ZEROCODEC_DECODER)       += zerocodec.o
 OBJS-$(CONFIG_ZLIB_DECODER)            += lcldec.o
 OBJS-$(CONFIG_ZLIB_ENCODER)            += lclenc.o
-OBJS-$(CONFIG_ZMBV_DECODER)            += zmbv.o
+OBJS-$(CONFIG_ZMBV_DECODER)            += zmbv.o inflate.o
 OBJS-$(CONFIG_ZMBV_ENCODER)            += zmbvenc.o
 
 # (AD)PCM decoders/encoders
diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
index 61857d0..fb8d0cd 100644
--- a/libavcodec/pngdec.c
+++ b/libavcodec/pngdec.c
@@ -34,7 +34,15 @@
 #include "pngdsp.h"
 #include "thread.h"
 
+#define USE_ZLIB CONFIG_ZLIB
+
+#if USE_ZLIB
 #include <zlib.h>
+#else
+#include "libavutil/mem.h"
+#include "get_bits.h"
+#include "inflate.h"
+#endif
 
 typedef struct PNGDecContext {
     PNGDSPContext dsp;
@@ -79,7 +87,13 @@ typedef struct PNGDecContext {
     int row_size; /* decompressed row size */
     int pass_row_size; /* decompress row size of the current pass */
     int y;
+#if USE_ZLIB
     z_stream zstream;
+#else
+    uint8_t *decomp_buffer;
+    unsigned decomp_buffer_size;
+    unsigned decomp_buffer_len;
+#endif
 } PNGDecContext;
 
 /* Mask to determine which pixels are valid in a pass */
@@ -389,8 +403,32 @@ the_end:;
     }
 }
 
+#if !USE_ZLIB
+static int decode_zbuf(const uint8_t *data, const uint8_t *data_end,
+                       uint8_t **text, unsigned *text_len);
+
+static int finalize_idat(PNGDecContext *s)
+{
+    uint8_t *out, *buffer, *buffer_end;
+    int outlen;
+    int ret = decode_zbuf(s->decomp_buffer, s->decomp_buffer + s->decomp_buffer_len, &out, &outlen);
+    s->decomp_buffer_len = 0;
+    if (ret < 0) return ret;
+    buffer = out;
+    buffer_end = buffer + outlen;
+    while (!(s->state & PNG_ALLIMAGE) && buffer_end - buffer >= s->crow_size) {
+        memcpy(s->crow_buf, buffer, s->crow_size);
+        buffer += s->crow_size;
+        png_handle_row(s);
+    }
+    free(out);
+    return 0;
+}
+#endif
+
 static int png_decode_idat(PNGDecContext *s, int length)
 {
+#if USE_ZLIB
     int ret;
     s->zstream.avail_in = FFMIN(length, bytestream2_get_bytes_left(&s->gb));
     s->zstream.next_in  = (unsigned char *)s->gb.buffer;
@@ -417,11 +455,33 @@ static int png_decode_idat(PNGDecContext *s, int length)
         }
     }
     return 0;
+#else
+    // Quick and dirty as ff_inflate cannot stop decoding at arbitrary points.
+    const uint8_t *data = s->gb.buffer;
+    uint8_t *tmp;
+    int len = FFMIN(length, bytestream2_get_bytes_left(&s->gb));
+    bytestream2_skip(&s->gb, length);
+    if (len == 0 || len > INT_MAX / 8) return AVERROR_INVALIDDATA;
+    if (s->decomp_buffer_len >= INT_MAX / 4) return AVERROR(ENOMEM);
+    tmp = av_fast_realloc(s->decomp_buffer, &s->decomp_buffer_size,
+                          s->decomp_buffer_len + len);
+    if (!tmp) {
+        av_freep(&s->decomp_buffer);
+        s->decomp_buffer_size = 0;
+        return AVERROR(ENOMEM);
+    }
+    s->decomp_buffer = tmp;
+    memcpy(s->decomp_buffer + s->decomp_buffer_len, data, len);
+    s->decomp_buffer_len += len;
+    return 0;
+#endif
 }
 
-static int decode_zbuf(AVBPrint *bp, const uint8_t *data,
-                       const uint8_t *data_end)
+static int decode_zbuf(const uint8_t *data, const uint8_t *data_end,
+                       uint8_t **text, unsigned *text_len)
 {
+#if USE_ZLIB
+    AVBPrint bp;
     z_stream zstream;
     unsigned char *buf;
     unsigned buf_size;
@@ -434,10 +494,10 @@ static int decode_zbuf(AVBPrint *bp, const uint8_t *data,
         return AVERROR_EXTERNAL;
     zstream.next_in  = (unsigned char *)data;
     zstream.avail_in = data_end - data;
-    av_bprint_init(bp, 0, -1);
+    av_bprint_init(&bp, 0, -1);
 
     while (zstream.avail_in > 0) {
-        av_bprint_get_buffer(bp, 1, &buf, &buf_size);
+        av_bprint_get_buffer(&bp, 1, &buf, &buf_size);
         if (!buf_size) {
             ret = AVERROR(ENOMEM);
             goto fail;
@@ -449,18 +509,62 @@ static int decode_zbuf(AVBPrint *bp, const uint8_t *data,
             ret = AVERROR_EXTERNAL;
             goto fail;
         }
-        bp->len += zstream.next_out - buf;
+        bp.len += zstream.next_out - buf;
         if (ret == Z_STREAM_END)
             break;
     }
     inflateEnd(&zstream);
-    bp->str[bp->len] = 0;
+    bp.str[bp.len] = 0;
+    *text_len = bp.len;
+    av_bprint_finalize(&bp, (char **)text);
+    if (!*text) {
+        *text_len = 0;
+        return AVERROR(ENOMEM);
+    }
     return 0;
 
 fail:
+    *text = NULL;
+    *text_len = 0;
     inflateEnd(&zstream);
-    av_bprint_finalize(bp, NULL);
+    av_bprint_finalize(&bp, NULL);
     return ret;
+#else
+    GetBitContext gb;
+    int res;
+    unsigned buf_size;
+    int out_len;
+    uint8_t *buf = NULL;
+    *text = NULL;
+    *text_len = 0;
+    if (data == data_end || data_end - data > INT_MAX / 8) return AVERROR_INVALIDDATA;
+    buf_size = data_end - data;
+    do {
+        buf_size *= 2;
+        av_freep(&buf);
+
+        res = init_get_bits8(&gb, data, data_end - data);
+        if (res < 0) break;
+
+        if (buf_size > INT_MAX / 4) { res = AVERROR(ENOMEM); break; }
+        buf = av_malloc(buf_size + FF_INFLATE_OUTPUT_PADDING);
+
+        out_len = buf_size;
+        res = ff_inflate(&gb, buf, &out_len, FF_INFLATE_HAS_HEADER);
+    } while (res == AVERROR_BUFFER_TOO_SMALL);
+    if (res == AVERROR_EOF) {
+        // TODO: print warning?
+        // Do not error out for zlib compatibility
+        res = 0;
+    }
+    if (res < 0) {
+        av_freep(&buf);
+        return res;
+    }
+    *text = buf;
+    *text_len = out_len;
+    return 0;
+#endif
 }
 
 static uint8_t *iso88591_to_utf8(const uint8_t *in, size_t size_in)
@@ -497,7 +601,6 @@ static int decode_text_chunk(PNGDecContext *s, uint32_t length, int compressed,
     const uint8_t *keyword_end = memchr(keyword, 0, data_end - keyword);
     uint8_t *kw_utf8 = NULL, *text, *txt_utf8 = NULL;
     unsigned text_len;
-    AVBPrint bp;
 
     if (!keyword_end)
         return AVERROR_INVALIDDATA;
@@ -509,12 +612,8 @@ static int decode_text_chunk(PNGDecContext *s, uint32_t length, int compressed,
         method = *(data++);
         if (method)
             return AVERROR_INVALIDDATA;
-        if ((ret = decode_zbuf(&bp, data, data_end)) < 0)
+        if ((ret = decode_zbuf(data, data_end, &text, &text_len)) < 0)
             return ret;
-        text_len = bp.len;
-        av_bprint_finalize(&bp, (char **)&text);
-        if (!text)
-            return AVERROR(ENOMEM);
     } else {
         text = (uint8_t *)data;
         text_len = data_end - text;
@@ -721,8 +820,10 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s,
 
         /* we want crow_buf+1 to be 16-byte aligned */
         s->crow_buf          = s->buffer + 15;
+#if USE_ZLIB
         s->zstream.avail_out = s->crow_size;
         s->zstream.next_out  = s->crow_buf;
+#endif
     }
 
     s->state |= PNG_IDAT;
@@ -1209,6 +1310,10 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
             break;
         }
         case MKTAG('I', 'E', 'N', 'D'):
+#if !USE_ZLIB
+            ret = finalize_idat(s);
+            if (ret < 0) goto fail;
+#endif
             if (!(s->state & PNG_ALLIMAGE))
                 av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");
             if (!(s->state & (PNG_ALLIMAGE|PNG_IDAT))) {
@@ -1314,6 +1419,7 @@ static int decode_frame_png(AVCodecContext *avctx,
 
     s->y = s->state = s->has_trns = 0;
 
+#if USE_ZLIB
     /* init the zlib */
     s->zstream.zalloc = ff_png_zalloc;
     s->zstream.zfree  = ff_png_zfree;
@@ -1323,6 +1429,9 @@ static int decode_frame_png(AVCodecContext *avctx,
         av_log(avctx, AV_LOG_ERROR, "inflateInit returned error %d\n", ret);
         return AVERROR_EXTERNAL;
     }
+#else
+    s->decomp_buffer_len = 0;
+#endif
 
     if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0)
         goto the_end;
@@ -1340,7 +1449,9 @@ static int decode_frame_png(AVCodecContext *avctx,
 
     ret = bytestream2_tell(&s->gb);
 the_end:
+#if USE_ZLIB
     inflateEnd(&s->zstream);
+#endif
     s->crow_buf = NULL;
     return ret;
 }
@@ -1363,21 +1474,27 @@ static int decode_frame_apng(AVCodecContext *avctx,
         if (!avctx->extradata_size)
             return AVERROR_INVALIDDATA;
 
+#if USE_ZLIB
         /* only init fields, there is no zlib use in extradata */
         s->zstream.zalloc = ff_png_zalloc;
         s->zstream.zfree  = ff_png_zfree;
+#endif
 
         bytestream2_init(&s->gb, avctx->extradata, avctx->extradata_size);
         if ((ret = decode_frame_common(avctx, s, p, avpkt)) < 0)
             goto end;
     }
 
+#if USE_ZLIB
     /* reset state for a new frame */
     if ((ret = inflateInit(&s->zstream)) != Z_OK) {
         av_log(avctx, AV_LOG_ERROR, "inflateInit returned error %d\n", ret);
         ret = AVERROR_EXTERNAL;
         goto end;
     }
+#else
+    s->decomp_buffer_len = 0;
+#endif
     s->y = 0;
     s->state &= ~(PNG_IDAT | PNG_ALLIMAGE);
     bytestream2_init(&s->gb, avpkt->data, avpkt->size);
@@ -1397,7 +1514,9 @@ static int decode_frame_apng(AVCodecContext *avctx,
     ret = bytestream2_tell(&s->gb);
 
 end:
+#if USE_ZLIB
     inflateEnd(&s->zstream);
+#endif
     return ret;
 }
 #endif
@@ -1493,6 +1612,10 @@ static av_cold int png_dec_end(AVCodecContext *avctx)
     s->last_row_size = 0;
     av_freep(&s->tmp_row);
     s->tmp_row_size = 0;
+#if !USE_ZLIB
+    av_freep(&s->decomp_buffer);
+    s->decomp_buffer_size = 0;
+#endif
 
     return 0;
 }
diff --git a/libavcodec/zmbv.c b/libavcodec/zmbv.c
index 25a1cd2..dcc5012 100644
--- a/libavcodec/zmbv.c
+++ b/libavcodec/zmbv.c
@@ -33,7 +33,14 @@
 #include "avcodec.h"
 #include "internal.h"
 
+#define USE_ZLIB CONFIG_ZLIB
+
+#if USE_ZLIB
 #include <zlib.h>
+#else
+#include "get_bits.h"
+#endif
+#include "inflate.h"
 
 #define ZMBV_KEYFRAME 1
 #define ZMBV_DELTAPAL 2
@@ -68,7 +75,9 @@ typedef struct ZmbvContext {
     int stride;
     int bw, bh, bx, by;
     int decomp_len;
+#if USE_ZLIB
     z_stream zstream;
+#endif
     int (*decode_intra)(struct ZmbvContext *c);
     int (*decode_xor)(struct ZmbvContext *c);
 } ZmbvContext;
@@ -405,7 +414,9 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac
     const uint8_t *buf = avpkt->data;
     int buf_size = avpkt->size;
     ZmbvContext * const c = avctx->priv_data;
+#if USE_ZLIB
     int zret = Z_OK; // Zlib return code
+#endif
     int len = buf_size;
     int hi_ver, lo_ver, ret;
 
@@ -488,11 +499,13 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac
             return AVERROR_PATCHWELCOME;
         }
 
+#if USE_ZLIB
         zret = inflateReset(&c->zstream);
         if (zret != Z_OK) {
             av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", zret);
             return AVERROR_UNKNOWN;
         }
+#endif
 
         c->cur  = av_realloc_f(c->cur, avctx->width * avctx->height,  (c->bpp / 8));
         c->prev = av_realloc_f(c->prev, avctx->width * avctx->height,  (c->bpp / 8));
@@ -520,6 +533,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac
         }
         memcpy(c->decomp_buf, buf, len);
     } else { // ZLIB-compressed data
+#if USE_ZLIB
         c->zstream.total_in = c->zstream.total_out = 0;
         c->zstream.next_in = (uint8_t*)buf;
         c->zstream.avail_in = len;
@@ -531,6 +545,25 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac
             return AVERROR_INVALIDDATA;
         }
         c->decomp_len = c->zstream.total_out;
+#else
+        GetBitContext gb;
+        int outlen = c->decomp_size + FF_INFLATE_DICT_SIZE;
+        int ret = init_get_bits8(&gb, buf, len);
+        int flags = FF_INFLATE_UPDATE_DICTIONARY | FF_INFLATE_SYNC_FLUSH |
+                    (c->flags & ZMBV_KEYFRAME ? FF_INFLATE_HAS_HEADER : FF_INFLATE_USE_DICTIONARY);
+        if (ret >= 0) {
+            ret = ff_inflate(&gb, c->decomp_buf - FF_INFLATE_DICT_SIZE, &outlen, flags);
+            if (ret == AVERROR_EOF) {
+                av_log(avctx, AV_LOG_WARNING, "Incomplete inflate input\n");
+                ret = 0;
+            }
+        }
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_ERROR, "inflate error\n");
+            return ret;
+        }
+        c->decomp_len = outlen;
+#endif
     }
     if (c->flags & ZMBV_KEYFRAME) {
         frame->key_frame = 1;
@@ -577,7 +610,9 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac
 static av_cold int decode_init(AVCodecContext *avctx)
 {
     ZmbvContext * const c = avctx->priv_data;
+#if USE_ZLIB
     int zret; // Zlib return code
+#endif
 
     c->avctx = avctx;
 
@@ -586,20 +621,24 @@ static av_cold int decode_init(AVCodecContext *avctx)
 
     c->bpp = avctx->bits_per_coded_sample;
 
+#if USE_ZLIB
     // Needed if zlib unused or init aborted before inflateInit
     memset(&c->zstream, 0, sizeof(z_stream));
+#endif
 
     c->decomp_size = (avctx->width + 255) * 4 * (avctx->height + 64);
 
     /* Allocate decompression buffer */
     if (c->decomp_size) {
-        if (!(c->decomp_buf = av_mallocz(c->decomp_size))) {
+        if (!(c->decomp_buf = av_mallocz(c->decomp_size + FF_INFLATE_DICT_SIZE + FF_INFLATE_OUTPUT_PADDING))) {
             av_log(avctx, AV_LOG_ERROR,
                    "Can't allocate decompression buffer.\n");
             return AVERROR(ENOMEM);
         }
+        c->decomp_buf += FF_INFLATE_DICT_SIZE;
     }
 
+#if USE_ZLIB
     c->zstream.zalloc = Z_NULL;
     c->zstream.zfree = Z_NULL;
     c->zstream.opaque = Z_NULL;
@@ -608,6 +647,7 @@ static av_cold int decode_init(AVCodecContext *avctx)
         av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret);
         return AVERROR_UNKNOWN;
     }
+#endif
 
     return 0;
 }
@@ -616,9 +656,12 @@ static av_cold int decode_end(AVCodecContext *avctx)
 {
     ZmbvContext * const c = avctx->priv_data;
 
-    av_freep(&c->decomp_buf);
+    av_free(c->decomp_buf - FF_INFLATE_DICT_SIZE);
+    c->decomp_buf = 0;
 
+#if USE_ZLIB
     inflateEnd(&c->zstream);
+#endif
     av_freep(&c->cur);
     av_freep(&c->prev);
 
-- 
2.7.0



More information about the ffmpeg-devel mailing list