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.doeffin...@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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel