[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