FFmpeg currently lacks a fallback inflate algorithm when zlib is not available. We have a lot of infrastructure for it already available though, like VLC code reading (though only in libavcodec, not libavutil). This is a hackish quick-and-dirty version. It certainly is not optimized, and I would want it to be mostly small/simple, not necessarily fast anyway as it would at most be a fallback. Is there interest in me cleaning up the code and integrating it as fallback, or are you all happy with zlib and I should drop it? I at least like that readability-wise this code is miles and miles ahead of zlib... --- libavcodec/inflate.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 libavcodec/inflate.c
diff --git a/libavcodec/inflate.c b/libavcodec/inflate.c new file mode 100644 index 0000000..0340d7c --- /dev/null +++ b/libavcodec/inflate.c @@ -0,0 +1,192 @@ +#define BITSTREAM_READER_LE + +#include "get_bits.h" +#include "libavutil/internal.h" +static uint16_t reverse(uint16_t x, int n) +{ + return (ff_reverse[x & 0xFF] << 8 | ff_reverse[x >> 8]) >> (16 - n); +} + +static int bits2codes(uint16_t *codes, const uint8_t *bits, int count) +{ + int len_counts[16] = {0}; + int code_starts[16] = {0}; + int i; + for (i = 0; i < count; i++) { + av_assert0(bits[i] < 16); + len_counts[bits[i]]++; + } + for (i = 2; i < 16; i++) + code_starts[i] = (code_starts[i - 1] + len_counts[i - 1]) << 1; + for (i = 0; i < count; i++) { + int n = bits[i]; + if (!n) continue; + codes[i] = code_starts[n]++; + codes[i] = reverse(codes[i], n); + } + for (i = 0; i < 16; i++) if (code_starts[i] > (1 << i)) return AVERROR_INVALIDDATA; + return 0; +} + +static void get_static_huff(VLC *v, VLC *dv) +{ + uint8_t main_bits[288 + 32]; + uint16_t main_codes[288 + 32]; + memset(main_bits, 8, 144); + memset(main_bits + 144, 9, 256 - 144); + memset(main_bits + 256, 7, 280 - 256); + memset(main_bits + 280, 0, 288 - 280); + memset(main_bits + 288, 5, 32); + + bits2codes(main_codes, main_bits, 288); + ff_free_vlc(v); + init_vlc(v, 9, 288, main_bits, 1, 1, main_codes, 2, 2, INIT_VLC_LE); + bits2codes(main_codes + 288, main_bits + 288, 32); + ff_free_vlc(dv); + init_vlc(dv, 9, 32, main_bits + 288, 1, 1, main_codes + 288, 2, 2, INIT_VLC_LE); +} + +static int build_dyn_huff(GetBitContext *gb, VLC *v, VLC *dv) +{ + VLC tmpv; + int i, max; + uint8_t main_bits[288 + 32] = {0}; + uint16_t main_codes[288 + 32]; + uint8_t clen_bits[19] = {0}; + uint16_t clen_codes[19]; + int hlit = get_bits(gb, 5) + 257; + int hdist = get_bits(gb, 5) + 1; + int hclen = get_bits(gb, 4) + 4; + for (i = 0; i < hclen; i++) + { + static const uint8_t map[19] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, 2, 14, 1, 15}; + clen_bits[map[i]] = get_bits(gb, 3); + } + bits2codes(clen_codes, clen_bits, 19); + init_vlc(&tmpv, 7, 19, clen_bits, 1, 1, clen_codes, 2, 2, INIT_VLC_LE); + i = 0; + max = hlit + hdist; + do { + int code = get_vlc2(gb, tmpv.table, 7, 1); + if (code < 0 || code > 18) return AVERROR_INVALIDDATA; + if (code < 16) { + main_bits[i++] = code; + } else if (code == 16) { + int cnt = get_bits(gb, 2) + 3; + if (cnt > max - i) return AVERROR_INVALIDDATA; + if (i == 0) return AVERROR_INVALIDDATA; + memset(main_bits + i, main_bits[i - 1], cnt); + i += cnt; + } else { + int cnt = code == 17 ? get_bits(gb, 3) + 3 : get_bits(gb, 7) + 11; + i += cnt; + } + } while (i < max); + ff_free_vlc(&tmpv); + bits2codes(main_codes, main_bits, hlit); + ff_free_vlc(v); + init_vlc(v, 9, hlit, main_bits, 1, 1, main_codes, 2, 2, INIT_VLC_LE); + bits2codes(main_codes + hlit, main_bits + hlit, hdist); + ff_free_vlc(dv); + init_vlc(dv, 9, hdist, main_bits + hlit, 1, 1, main_codes + hlit, 2, 2, INIT_VLC_LE); +} + +static int inflate_block(GetBitContext *gb, const VLC *v, const VLC *dv, uint8_t *out, uint8_t *out_end) +{ + uint8_t *start = out; + int code; + do { + code = get_vlc2(gb, v->table, 9, 2); + if (code < 0 || code > 285 || get_bits_left(gb) < 0) return AVERROR_INVALIDDATA; + if (code < 256) { + if (out >= out_end) return -1; + *out++ = code; + } else if (code > 256) { + // Note: Overall length 258 has two encodings. + // We also accept the pointless inefficient one. + static const uint8_t len_tab[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 255}; + static const uint16_t dist_tab[] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; + int len; + int dist; + + code -= 257; + len = len_tab[code] + 3; + if (code >= 8 && code < 28) + len += get_bits(gb, (code >> 2) - 1); + + code = get_vlc2(gb, dv->table, 9, 2); + if (code < 0 || code > 29 || get_bits_left(gb) < 0) return AVERROR_INVALIDDATA; + dist = dist_tab[code]; + if (code > 3) { + dist += get_bits(gb, (code >> 1) - 1); + } + + if (len > out_end - out) return -1; + if (dist > out - start) return AVERROR_INVALIDDATA; + av_memcpy_backptr(out, dist, len); + out += len; + } + } while (code != 256); + return out - start; +} + +int ff_inflate(const uint8_t *in, int inlen, uint8_t *out, int outlen) +{ + int final, type, len = 0; + GetBitContext gb; + VLC v = { .table = NULL }; + VLC dv = { .table = NULL }; + uint8_t *out_end = out + outlen; + int ret = init_get_bits8(&gb, in, inlen); + if (ret < 0) return ret; + do { + final = get_bits1(&gb); + type = get_bits(&gb, 2); + switch (type) { + case 0: { + const uint8_t *src = align_get_bits(&gb); + ret = get_bits(&gb, 16); + skip_bits(&gb, 16); + if (ret > outlen - len) return -1; + skip_bits_long(&gb, 8*ret); + if (get_bits_left(&gb) < 0) return AVERROR_INVALIDDATA; + memcpy(out + len, src, ret); + len += ret; + break; + } + case 1: + get_static_huff(&v, &dv); + ret = inflate_block(&gb, &v, &dv, out, out_end); + break; + case 2: + ret = build_dyn_huff(&gb, &v, &dv); + if (ret < 0) return ret; + ret = inflate_block(&gb, &v, &dv, out, out_end); + break; + case 3: + return AVERROR_INVALIDDATA; + } + if (get_bits_left(&gb) < 0) return AVERROR_INVALIDDATA; + if (ret < 0) return ret; + if (ret > INT_MAX - len) return -1; + len += ret; + } while (!final); + return len; +} + +int main(int argc, char **argv) +{ + uint8_t in[4096]; + uint8_t out[8*4096] = {0}; + FILE *f = fopen(argv[1], "rb"); + int flen = fread(in, 1, sizeof(in), f); + int outs = ff_inflate(in + 2, flen - 2, out, sizeof(out) - 1); + if (outs < 0) printf("failed!\n"); + if (outs > 0) { + out[outs] = 0; + printf("%s", out); + } + return 0; +} -- 2.7.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel