You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-04 22:03:09 +02:00
lavc: APV decoder
This commit is contained in:
200
libavcodec/apv_entropy.c
Normal file
200
libavcodec/apv_entropy.c
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "apv.h"
|
||||
#include "apv_decode.h"
|
||||
|
||||
|
||||
void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut)
|
||||
{
|
||||
const int code_len = APV_VLC_LUT_BITS;
|
||||
const int lut_size = APV_VLC_LUT_SIZE;
|
||||
|
||||
for (int k = 0; k <= 5; k++) {
|
||||
for (unsigned int code = 0; code < lut_size; code++) {
|
||||
APVVLCLUTEntry *ent = &decode_lut->lut[k][code];
|
||||
unsigned int first_bit = code & (1 << code_len - 1);
|
||||
unsigned int remaining_bits = code ^ first_bit;
|
||||
|
||||
if (first_bit) {
|
||||
ent->consume = 1 + k;
|
||||
ent->result = remaining_bits >> (code_len - k - 1);
|
||||
ent->more = 0;
|
||||
} else {
|
||||
unsigned int second_bit = code & (1 << code_len - 2);
|
||||
remaining_bits ^= second_bit;
|
||||
|
||||
if (second_bit) {
|
||||
unsigned int bits_left = code_len - 2;
|
||||
unsigned int first_set = bits_left - av_log2(remaining_bits);
|
||||
unsigned int last_bits = first_set - 1 + k;
|
||||
|
||||
if (first_set + last_bits <= bits_left) {
|
||||
// Whole code fits here.
|
||||
ent->consume = 2 + first_set + last_bits;
|
||||
ent->result = ((2 << k) +
|
||||
(((1 << first_set - 1) - 1) << k) +
|
||||
((code >> bits_left - first_set - last_bits) & (1 << last_bits) - 1));
|
||||
ent->more = 0;
|
||||
} else {
|
||||
// Need to read more, collapse to default.
|
||||
ent->consume = 2;
|
||||
ent->more = 1;
|
||||
}
|
||||
} else {
|
||||
ent->consume = 2 + k;
|
||||
ent->result = (1 << k) + (remaining_bits >> (code_len - k - 2));
|
||||
ent->more = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
av_always_inline
|
||||
static unsigned int apv_read_vlc(GetBitContext *gbc, int k_param,
|
||||
const APVVLCLUT *lut)
|
||||
{
|
||||
unsigned int next_bits;
|
||||
const APVVLCLUTEntry *ent;
|
||||
|
||||
next_bits = show_bits(gbc, APV_VLC_LUT_BITS);
|
||||
ent = &lut->lut[k_param][next_bits];
|
||||
|
||||
if (ent->more) {
|
||||
unsigned int leading_zeroes;
|
||||
|
||||
skip_bits(gbc, ent->consume);
|
||||
|
||||
next_bits = show_bits(gbc, 16);
|
||||
leading_zeroes = 15 - av_log2(next_bits);
|
||||
|
||||
skip_bits(gbc, leading_zeroes + 1);
|
||||
|
||||
return (2 << k_param) +
|
||||
((1 << leading_zeroes) - 1) * (1 << k_param) +
|
||||
get_bits(gbc, leading_zeroes + k_param);
|
||||
} else {
|
||||
skip_bits(gbc, ent->consume);
|
||||
return ent->result;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ff_apv_read_vlc(GetBitContext *gbc, int k_param,
|
||||
const APVVLCLUT *lut)
|
||||
{
|
||||
return apv_read_vlc(gbc, k_param, lut);
|
||||
}
|
||||
|
||||
int ff_apv_entropy_decode_block(int16_t *coeff,
|
||||
GetBitContext *gbc,
|
||||
APVEntropyState *state)
|
||||
{
|
||||
const APVVLCLUT *lut = state->decode_lut;
|
||||
int k_param;
|
||||
|
||||
// DC coefficient.
|
||||
{
|
||||
int abs_dc_coeff_diff;
|
||||
int sign_dc_coeff_diff;
|
||||
int dc_coeff;
|
||||
|
||||
k_param = av_clip(state->prev_dc_diff >> 1, 0, 5);
|
||||
abs_dc_coeff_diff = apv_read_vlc(gbc, k_param, lut);
|
||||
|
||||
if (abs_dc_coeff_diff > 0)
|
||||
sign_dc_coeff_diff = get_bits1(gbc);
|
||||
else
|
||||
sign_dc_coeff_diff = 0;
|
||||
|
||||
if (sign_dc_coeff_diff)
|
||||
dc_coeff = state->prev_dc - abs_dc_coeff_diff;
|
||||
else
|
||||
dc_coeff = state->prev_dc + abs_dc_coeff_diff;
|
||||
|
||||
if (dc_coeff < APV_MIN_TRANS_COEFF ||
|
||||
dc_coeff > APV_MAX_TRANS_COEFF) {
|
||||
av_log(state->log_ctx, AV_LOG_ERROR,
|
||||
"Out-of-range DC coefficient value: %d "
|
||||
"(from prev_dc %d abs_dc_coeff_diff %d sign_dc_coeff_diff %d)\n",
|
||||
dc_coeff, state->prev_dc, abs_dc_coeff_diff, sign_dc_coeff_diff);
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
coeff[0] = dc_coeff;
|
||||
|
||||
state->prev_dc = dc_coeff;
|
||||
state->prev_dc_diff = abs_dc_coeff_diff;
|
||||
}
|
||||
|
||||
// AC coefficients.
|
||||
{
|
||||
int scan_pos = 1;
|
||||
int first_ac = 1;
|
||||
int prev_level = state->prev_1st_ac_level;
|
||||
int prev_run = 0;
|
||||
|
||||
do {
|
||||
int coeff_zero_run;
|
||||
|
||||
k_param = av_clip(prev_run >> 2, 0, 2);
|
||||
coeff_zero_run = apv_read_vlc(gbc, k_param, lut);
|
||||
|
||||
if (coeff_zero_run > APV_BLK_COEFFS - scan_pos) {
|
||||
av_log(state->log_ctx, AV_LOG_ERROR,
|
||||
"Out-of-range zero-run value: %d (at scan pos %d)\n",
|
||||
coeff_zero_run, scan_pos);
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
for (int i = 0; i < coeff_zero_run; i++) {
|
||||
coeff[ff_zigzag_direct[scan_pos]] = 0;
|
||||
++scan_pos;
|
||||
}
|
||||
prev_run = coeff_zero_run;
|
||||
|
||||
if (scan_pos < APV_BLK_COEFFS) {
|
||||
int abs_ac_coeff_minus1;
|
||||
int sign_ac_coeff;
|
||||
int level;
|
||||
|
||||
k_param = av_clip(prev_level >> 2, 0, 4);
|
||||
abs_ac_coeff_minus1 = apv_read_vlc(gbc, k_param, lut);
|
||||
sign_ac_coeff = get_bits(gbc, 1);
|
||||
|
||||
if (sign_ac_coeff)
|
||||
level = -abs_ac_coeff_minus1 - 1;
|
||||
else
|
||||
level = abs_ac_coeff_minus1 + 1;
|
||||
|
||||
coeff[ff_zigzag_direct[scan_pos]] = level;
|
||||
|
||||
prev_level = abs_ac_coeff_minus1 + 1;
|
||||
if (first_ac) {
|
||||
state->prev_1st_ac_level = prev_level;
|
||||
first_ac = 0;
|
||||
}
|
||||
|
||||
++scan_pos;
|
||||
}
|
||||
|
||||
} while (scan_pos < APV_BLK_COEFFS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user