1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-04 22:03:09 +02:00

avcodec/webp: Switch to ff_vlc_init_from_lengths()

The earlier code would traverse over the code lengths
mutliple times (namely max_length + 1 times - once to get
the maximum length and once for each max_length to assign
codes) before calling ff_vlc_init_sparse() (which may traverse
them twice and sort them). The new code only traverses them once
(+ the one time in ff_vlc_init_from_lengths()).

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
This commit is contained in:
Andreas Rheinhardt
2025-04-18 19:29:41 +02:00
parent d60445258c
commit e0df21b8c3

View File

@ -253,64 +253,58 @@ static int huff_reader_get_symbol(HuffReader *r, GetBitContext *gb)
} }
static int huff_reader_build_canonical(HuffReader *r, const uint8_t *code_lengths, static int huff_reader_build_canonical(HuffReader *r, const uint8_t *code_lengths,
int alphabet_size) uint16_t len_counts[MAX_HUFFMAN_CODE_LENGTH + 1],
int alphabet_size, void *logctx)
{ {
int len = 0, sym, code = 0, ret; uint16_t *syms;
int max_code_length = 0; uint8_t *lens;
uint16_t *codes; unsigned nb_codes = 0;
int ret;
// Count the number of symbols of each length and transform len_counts
// into an array of offsets.
for (int len = 1; len <= MAX_HUFFMAN_CODE_LENGTH; ++len) {
unsigned cnt = len_counts[len];
len_counts[len] = nb_codes;
nb_codes += cnt;
}
if (nb_codes <= 1) {
if (nb_codes == 1) {
/* special-case 1 symbol since the vlc reader cannot handle it */ /* special-case 1 symbol since the vlc reader cannot handle it */
for (sym = 0; sym < alphabet_size; sym++) {
if (code_lengths[sym] > 0) {
len++;
code = sym;
if (len > 1)
break;
}
}
if (len == 1) {
r->nb_symbols = 1; r->nb_symbols = 1;
r->simple_symbols[0] = code;
r->simple = 1; r->simple = 1;
for (int sym = 0;; ++sym) {
av_assert1(sym < alphabet_size);
if (code_lengths[sym]) {
r->simple_symbols[0] = sym;
return 0; return 0;
} }
for (sym = 0; sym < alphabet_size; sym++)
max_code_length = FFMAX(max_code_length, code_lengths[sym]);
if (max_code_length == 0)
return AVERROR(EINVAL);
codes = av_malloc_array(alphabet_size, sizeof(*codes));
if (!codes)
return AVERROR(ENOMEM);
code = 0;
r->nb_symbols = 0;
for (len = 1; len <= max_code_length; len++) {
for (sym = 0; sym < alphabet_size; sym++) {
if (code_lengths[sym] != len)
continue;
codes[sym] = code++;
r->nb_symbols++;
} }
code <<= 1;
} }
if (!r->nb_symbols) { // No symbols
av_free(codes);
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
} }
ret = vlc_init(&r->vlc, 8, alphabet_size, syms = av_malloc_array(nb_codes, sizeof(*syms) + sizeof(*lens));
code_lengths, sizeof(*code_lengths), sizeof(*code_lengths), if (!syms)
codes, sizeof(*codes), sizeof(*codes), VLC_INIT_OUTPUT_LE); return AVERROR(ENOMEM);
if (ret < 0) { lens = (uint8_t*)(syms + nb_codes);
av_free(codes);
return ret; for (int sym = 0; sym < alphabet_size; ++sym) {
if (code_lengths[sym]) {
unsigned idx = len_counts[code_lengths[sym]]++;
syms[idx] = sym;
lens[idx] = code_lengths[sym];
} }
}
ret = ff_vlc_init_from_lengths(&r->vlc, 8, nb_codes, lens, 1,
syms, 2, 2, 0, VLC_INIT_OUTPUT_LE, logctx);
av_free(syms);
if (ret < 0)
return ret;
r->simple = 0; r->simple = 0;
av_free(codes);
return 0; return 0;
} }
@ -335,20 +329,24 @@ static int read_huffman_code_normal(WebPContext *s, HuffReader *hc,
HuffReader code_len_hc = { { 0 }, 0, 0, { 0 } }; HuffReader code_len_hc = { { 0 }, 0, 0, { 0 } };
uint8_t *code_lengths; uint8_t *code_lengths;
uint8_t code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 }; uint8_t code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 };
int i, symbol, max_symbol, prev_code_len, ret; uint16_t len_counts[MAX_HUFFMAN_CODE_LENGTH + 1] = { 0 };
int symbol, max_symbol, prev_code_len, ret;
int num_codes = 4 + get_bits(&s->gb, 4); int num_codes = 4 + get_bits(&s->gb, 4);
av_assert1(num_codes <= NUM_CODE_LENGTH_CODES); av_assert1(num_codes <= NUM_CODE_LENGTH_CODES);
for (i = 0; i < num_codes; i++) for (int i = 0; i < num_codes; i++) {
code_length_code_lengths[code_length_code_order[i]] = get_bits(&s->gb, 3); unsigned len = get_bits(&s->gb, 3);
code_length_code_lengths[code_length_code_order[i]] = len;
len_counts[len]++;
}
ret = huff_reader_build_canonical(&code_len_hc, code_length_code_lengths, ret = huff_reader_build_canonical(&code_len_hc, code_length_code_lengths, len_counts,
NUM_CODE_LENGTH_CODES); NUM_CODE_LENGTH_CODES, s->avctx);
if (ret < 0) if (ret < 0)
return ret; return ret;
code_lengths = av_mallocz(alphabet_size); code_lengths = av_malloc(alphabet_size);
if (!code_lengths) { if (!code_lengths) {
ret = AVERROR(ENOMEM); ret = AVERROR(ENOMEM);
goto finish; goto finish;
@ -369,6 +367,7 @@ static int read_huffman_code_normal(WebPContext *s, HuffReader *hc,
prev_code_len = 8; prev_code_len = 8;
symbol = 0; symbol = 0;
memset(len_counts, 0, sizeof(len_counts));
while (symbol < alphabet_size) { while (symbol < alphabet_size) {
int code_len; int code_len;
@ -378,6 +377,7 @@ static int read_huffman_code_normal(WebPContext *s, HuffReader *hc,
if (code_len < 16U) { if (code_len < 16U) {
/* Code length code [0..15] indicates literal code lengths. */ /* Code length code [0..15] indicates literal code lengths. */
code_lengths[symbol++] = code_len; code_lengths[symbol++] = code_len;
len_counts[code_len]++;
if (code_len) if (code_len)
prev_code_len = code_len; prev_code_len = code_len;
} else { } else {
@ -392,6 +392,7 @@ static int read_huffman_code_normal(WebPContext *s, HuffReader *hc,
* non-zero value has been emitted, a value of 8 is repeated. */ * non-zero value has been emitted, a value of 8 is repeated. */
repeat = 3 + get_bits(&s->gb, 2); repeat = 3 + get_bits(&s->gb, 2);
length = prev_code_len; length = prev_code_len;
len_counts[length] += repeat;
break; break;
case 17: case 17:
/* Code 17 emits a streak of zeros [3..10], i.e., /* Code 17 emits a streak of zeros [3..10], i.e.,
@ -416,7 +417,8 @@ static int read_huffman_code_normal(WebPContext *s, HuffReader *hc,
} }
} }
ret = huff_reader_build_canonical(hc, code_lengths, alphabet_size); ret = huff_reader_build_canonical(hc, code_lengths, len_counts,
symbol, s->avctx);
finish: finish:
ff_vlc_free(&code_len_hc.vlc); ff_vlc_free(&code_len_hc.vlc);