--- configure | 1 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/apv_decode.c | 331 +++++++++++++++++++++++++++++++++++++++ libavcodec/apv_decode.h | 80 ++++++++++ libavcodec/apv_dsp.c | 136 ++++++++++++++++ libavcodec/apv_dsp.h | 37 +++++ libavcodec/apv_entropy.c | 200 +++++++++++++++++++++++ 8 files changed, 787 insertions(+) create mode 100644 libavcodec/apv_decode.c create mode 100644 libavcodec/apv_decode.h create mode 100644 libavcodec/apv_dsp.c create mode 100644 libavcodec/apv_dsp.h create mode 100644 libavcodec/apv_entropy.c
diff --git a/configure b/configure index ca404d2797..ee270b770c 100755 --- a/configure +++ b/configure @@ -2935,6 +2935,7 @@ apng_decoder_select="inflate_wrapper" apng_encoder_select="deflate_wrapper llvidencdsp" aptx_encoder_select="audio_frame_queue" aptx_hd_encoder_select="audio_frame_queue" +apv_decoder_select="cbs_apv" asv1_decoder_select="blockdsp bswapdsp idctdsp" asv1_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp" asv2_decoder_select="blockdsp bswapdsp idctdsp" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index a5f5c4e904..e674671460 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -244,6 +244,7 @@ OBJS-$(CONFIG_APTX_HD_DECODER) += aptxdec.o aptx.o OBJS-$(CONFIG_APTX_HD_ENCODER) += aptxenc.o aptx.o OBJS-$(CONFIG_APNG_DECODER) += png.o pngdec.o pngdsp.o OBJS-$(CONFIG_APNG_ENCODER) += png.o pngenc.o +OBJS-$(CONFIG_APV_DECODER) += apv_decode.o apv_entropy.o apv_dsp.o OBJS-$(CONFIG_ARBC_DECODER) += arbc.o OBJS-$(CONFIG_ARGO_DECODER) += argo.o OBJS-$(CONFIG_SSA_DECODER) += assdec.o ass.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index f10519617e..09f06c71d6 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -47,6 +47,7 @@ extern const FFCodec ff_anm_decoder; extern const FFCodec ff_ansi_decoder; extern const FFCodec ff_apng_encoder; extern const FFCodec ff_apng_decoder; +extern const FFCodec ff_apv_decoder; extern const FFCodec ff_arbc_decoder; extern const FFCodec ff_argo_decoder; extern const FFCodec ff_asv1_encoder; diff --git a/libavcodec/apv_decode.c b/libavcodec/apv_decode.c new file mode 100644 index 0000000000..db1ae9756d --- /dev/null +++ b/libavcodec/apv_decode.c @@ -0,0 +1,331 @@ +/* + * 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 "libavutil/mem_internal.h" +#include "libavutil/pixdesc.h" + +#include "apv.h" +#include "apv_decode.h" +#include "apv_dsp.h" +#include "avcodec.h" +#include "cbs.h" +#include "cbs_apv.h" +#include "codec_internal.h" +#include "decode.h" +#include "thread.h" + + +typedef struct APVDecodeContext { + CodedBitstreamContext *cbc; + APVDSPContext dsp; + + CodedBitstreamFragment au; + APVDerivedTileInfo tile_info; + + APVVLCLUT decode_lut; + + AVFrame *output_frame; +} APVDecodeContext; + +static const enum AVPixelFormat apv_format_table[5][5] = { + { AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16 }, + { 0 }, // 4:2:0 is not valid. + { AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUV422P16 }, + { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUV444P16 }, + { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUVA444P16 }, +}; + +static int apv_decode_check_format(AVCodecContext *avctx, + const APVRawFrameHeader *header) +{ + int err, bit_depth; + + avctx->profile = header->frame_info.profile_idc; + avctx->level = header->frame_info.level_idc; + + bit_depth = header->frame_info.bit_depth_minus8 + 8; + if (bit_depth < 8 || bit_depth > 16 || bit_depth % 2) { + avpriv_request_sample(avctx, "Bit depth %d", bit_depth); + return AVERROR_PATCHWELCOME; + } + avctx->pix_fmt = + apv_format_table[header->frame_info.chroma_format_idc][bit_depth - 4 >> 2]; + + err = ff_set_dimensions(avctx, + FFALIGN(header->frame_info.frame_width, 16), + FFALIGN(header->frame_info.frame_height, 16)); + if (err < 0) { + // Unsupported frame size. + return err; + } + avctx->width = header->frame_info.frame_width; + avctx->height = header->frame_info.frame_height; + + avctx->sample_aspect_ratio = (AVRational){ 1, 1 }; + + avctx->color_primaries = header->color_primaries; + avctx->color_trc = header->transfer_characteristics; + avctx->colorspace = header->matrix_coefficients; + avctx->color_range = header->full_range_flag ? AVCOL_RANGE_JPEG + : AVCOL_RANGE_MPEG; + avctx->chroma_sample_location = AVCHROMA_LOC_TOPLEFT; + + avctx->refs = 0; + avctx->has_b_frames = 0; + + return 0; +} + +static av_cold int apv_decode_init(AVCodecContext *avctx) +{ + APVDecodeContext *apv = avctx->priv_data; + int err; + + err = ff_cbs_init(&apv->cbc, AV_CODEC_ID_APV, avctx); + if (err < 0) + return err; + + ff_apv_entropy_build_decode_lut(&apv->decode_lut); + + ff_apv_dsp_init(&apv->dsp); + + if (avctx->extradata) { + av_log(avctx, AV_LOG_WARNING, + "APV does not support extradata.\n"); + } + + return 0; +} + +static av_cold int apv_decode_close(AVCodecContext *avctx) +{ + APVDecodeContext *apv = avctx->priv_data; + + ff_cbs_fragment_free(&apv->au); + ff_cbs_close(&apv->cbc); + + return 0; +} + +static int apv_decode_block(AVCodecContext *avctx, + void *output, + ptrdiff_t pitch, + GetBitContext *gbc, + APVEntropyState *entropy_state, + int bit_depth, + int qp_shift, + const uint16_t *qmatrix) +{ + APVDecodeContext *apv = avctx->priv_data; + int err; + + LOCAL_ALIGNED_32(int16_t, coeff, [64]); + + err = ff_apv_entropy_decode_block(coeff, gbc, entropy_state); + if (err < 0) + return 0; + + apv->dsp.decode_transquant(output, pitch, + coeff, qmatrix, + bit_depth, qp_shift); + + return 0; +} + +static int apv_decode_tile_component(AVCodecContext *avctx, void *data, + int job, int thread) +{ + APVRawFrame *input = data; + APVDecodeContext *apv = avctx->priv_data; + const CodedBitstreamAPVContext *apv_cbc = apv->cbc->priv_data; + const APVDerivedTileInfo *tile_info = &apv_cbc->tile_info; + + int tile_index = job / apv_cbc->num_comp; + int comp_index = job % apv_cbc->num_comp; + + const AVPixFmtDescriptor *pix_fmt_desc = + av_pix_fmt_desc_get(avctx->pix_fmt); + + int sub_w = comp_index == 0 ? 1 : pix_fmt_desc->log2_chroma_w + 1; + int sub_h = comp_index == 0 ? 1 : pix_fmt_desc->log2_chroma_h + 1; + + APVRawTile *tile = &input->tile[tile_index]; + + int tile_y = tile_index / tile_info->tile_cols; + int tile_x = tile_index % tile_info->tile_cols; + + int tile_start_x = tile_info->col_starts[tile_x]; + int tile_start_y = tile_info->row_starts[tile_y]; + + int tile_width = tile_info->col_starts[tile_x + 1] - tile_start_x; + int tile_height = tile_info->row_starts[tile_y + 1] - tile_start_y; + + int tile_mb_width = tile_width / APV_MB_WIDTH; + int tile_mb_height = tile_height / APV_MB_HEIGHT; + + int blk_mb_width = 2 / sub_w; + int blk_mb_height = 2 / sub_h; + + int bit_depth; + int qp_shift; + LOCAL_ALIGNED_32(uint16_t, qmatrix_scaled, [64]); + + GetBitContext gbc; + + APVEntropyState entropy_state = { + .log_ctx = avctx, + .decode_lut = &apv->decode_lut, + .prev_dc = 0, + .prev_dc_diff = 20, + .prev_1st_ac_level = 0, + }; + + init_get_bits8(&gbc, tile->tile_data[comp_index], + tile->tile_header.tile_data_size[comp_index]); + + // Combine the bitstream quantisation matrix with the qp scaling + // in advance. (Including qp_shift as well would overflow 16 bits.) + // Fix the row ordering at the same time. + { + static const uint8_t apv_level_scale[6] = { 40, 45, 51, 57, 64, 71 }; + int qp = tile->tile_header.tile_qp[comp_index]; + int level_scale = apv_level_scale[qp % 6]; + + bit_depth = apv_cbc->bit_depth; + qp_shift = qp / 6; + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) + qmatrix_scaled[y * 8 + x] = level_scale * + input->frame_header.quantization_matrix.q_matrix[comp_index][x][y]; + } + } + + for (int mb_y = 0; mb_y < tile_mb_height; mb_y++) { + for (int mb_x = 0; mb_x < tile_mb_width; mb_x++) { + for (int blk_y = 0; blk_y < blk_mb_height; blk_y++) { + for (int blk_x = 0; blk_x < blk_mb_width; blk_x++) { + int frame_y = (tile_start_y + + APV_MB_HEIGHT * mb_y + + APV_TR_SIZE * blk_y) / sub_h; + int frame_x = (tile_start_x + + APV_MB_WIDTH * mb_x + + APV_TR_SIZE * blk_x) / sub_w; + + ptrdiff_t frame_pitch = apv->output_frame->linesize[comp_index]; + uint8_t *block_start = apv->output_frame->data[comp_index] + + frame_y * frame_pitch + 2 * frame_x; + + apv_decode_block(avctx, + block_start, frame_pitch, + &gbc, &entropy_state, + bit_depth, + qp_shift, + qmatrix_scaled); + } + } + } + } + + av_log(avctx, AV_LOG_DEBUG, + "Decoded tile %d component %d: %dx%d MBs starting at (%d,%d)\n", + tile_index, comp_index, tile_mb_width, tile_mb_height, + tile_start_x, tile_start_y); + + return 0; +} + +static int apv_decode(AVCodecContext *avctx, AVFrame *output, + APVRawFrame *input) +{ + APVDecodeContext *apv = avctx->priv_data; + const CodedBitstreamAPVContext *apv_cbc = apv->cbc->priv_data; + const APVDerivedTileInfo *tile_info = &apv_cbc->tile_info; + int err, job_count; + + err = apv_decode_check_format(avctx, &input->frame_header); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Unsupported format parameters.\n"); + return err; + } + + err = ff_thread_get_buffer(avctx, output, 0); + if (err) { + av_log(avctx, AV_LOG_ERROR, "No output frame supplied.\n"); + return err; + } + + apv->output_frame = output; + + // Each component within a tile is independent of every other, + // so we can decode all in parallel. + job_count = tile_info->num_tiles * apv_cbc->num_comp; + + avctx->execute2(avctx, apv_decode_tile_component, + input, NULL, job_count); + + return 0; +} + +static int apv_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *packet) +{ + APVDecodeContext *apv = avctx->priv_data; + CodedBitstreamFragment *au = &apv->au; + int err; + + err = ff_cbs_read_packet(apv->cbc, au, packet); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to read packet.\n"); + return err; + } + + for (int i = 0; i < au->nb_units; i++) { + CodedBitstreamUnit *pbu = &au->units[i]; + + switch (pbu->type) { + case APV_PBU_PRIMARY_FRAME: + err = apv_decode(avctx, frame, pbu->content); + if (err < 0) + return err; + *got_frame = 1; + break; + default: + av_log(avctx, AV_LOG_WARNING, + "Ignoring unsupported PBU type %d.\n", pbu->type); + } + } + + ff_cbs_fragment_reset(au); + + return packet->size; +} + +const FFCodec ff_apv_decoder = { + .p.name = "apv", + CODEC_LONG_NAME("Advanced Professional Video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_APV, + .priv_data_size = sizeof(APVDecodeContext), + .init = apv_decode_init, + .close = apv_decode_close, + FF_CODEC_DECODE_CB(apv_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_FRAME_THREADS, +}; diff --git a/libavcodec/apv_decode.h b/libavcodec/apv_decode.h new file mode 100644 index 0000000000..34c6176ea0 --- /dev/null +++ b/libavcodec/apv_decode.h @@ -0,0 +1,80 @@ +/* + * 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 + */ + +#ifndef AVCODEC_APV_DECODE_H +#define AVCODEC_APV_DECODE_H + +#include <stdint.h> + +#include "apv.h" +#include "avcodec.h" +#include "get_bits.h" + + +// Number of bits in the entropy look-up tables. +// It may be desirable to tune this per-architecture, as a larger LUT +// trades greater memory use for fewer instructions. +// (N bits -> 24*2^N bytes of tables; 9 -> 12KB of tables.) +#define APV_VLC_LUT_BITS 9 +#define APV_VLC_LUT_SIZE (1 << APV_VLC_LUT_BITS) + +typedef struct APVVLCLUTEntry { + uint16_t result; // Return value if not reading more. + uint8_t consume; // Number of bits to consume. + uint8_t more; // Whether to read additional bits. +} APVVLCLUTEntry; + +typedef struct APVVLCLUT { + APVVLCLUTEntry lut[6][APV_VLC_LUT_SIZE]; +} APVVLCLUT; + +typedef struct APVEntropyState { + void *log_ctx; + + const APVVLCLUT *decode_lut; + + int16_t prev_dc; + int16_t prev_dc_diff; + int16_t prev_1st_ac_level; +} APVEntropyState; + + +/** + * Build the decoder VLC look-up table. + */ +void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut); + +/** + * Entropy decode a single 8x8 block to coefficients. + * + * Outputs in block order (dezigzag already applied). + */ +int ff_apv_entropy_decode_block(int16_t *coeff, + GetBitContext *gbc, + APVEntropyState *state); + +/** + * Read a single APV VLC code. + * + * This entrypoint is exposed for testing. + */ +unsigned int ff_apv_read_vlc(GetBitContext *gbc, int k_param, + const APVVLCLUT *lut); + + +#endif /* AVCODEC_APV_DECODE_H */ diff --git a/libavcodec/apv_dsp.c b/libavcodec/apv_dsp.c new file mode 100644 index 0000000000..fe11cd6b94 --- /dev/null +++ b/libavcodec/apv_dsp.c @@ -0,0 +1,136 @@ +/* + * 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 <stdint.h> + +#include "config.h" +#include "libavutil/attributes.h" +#include "libavutil/common.h" + +#include "apv.h" +#include "apv_dsp.h" + + +static const int8_t apv_trans_matrix[8][8] = { + { 64, 64, 64, 64, 64, 64, 64, 64 }, + { 89, 75, 50, 18, -18, -50, -75, -89 }, + { 84, 35, -35, -84, -84, -35, 35, 84 }, + { 75, -18, -89, -50, 50, 89, 18, -75 }, + { 64, -64, -64, 64, 64, -64, -64, 64 }, + { 50, -89, 18, 75, -75, -18, 89, -50 }, + { 35, -84, 84, -35, -35, 84, -84, 35 }, + { 18, -50, 75, -89, 89, -75, 50, -18 }, +}; + +static void apv_decode_transquant_c(void *output, + ptrdiff_t pitch, + const int16_t *input_flat, + const int16_t *qmatrix_flat, + int bit_depth, + int qp_shift) +{ + const int16_t (*input)[8] = (const int16_t(*)[8])input_flat; + const int16_t (*qmatrix)[8] = (const int16_t(*)[8])qmatrix_flat; + + int16_t scaled_coeff[8][8]; + int32_t recon_sample[8][8]; + + // Dequant. + { + // Note that level_scale was already combined into qmatrix + // before we got here. + int bd_shift = bit_depth + 3 - 5; + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + int coeff = (((input[y][x] * qmatrix[y][x]) << qp_shift) + + (1 << (bd_shift - 1))) >> bd_shift; + + scaled_coeff[y][x] = + av_clip(coeff, APV_MIN_TRANS_COEFF, + APV_MAX_TRANS_COEFF); + } + } + } + + // Transform. + { + int32_t tmp[8][8]; + + // Vertical transform of columns. + for (int x = 0; x < 8; x++) { + for (int i = 0; i < 8; i++) { + int sum = 0; + for (int j = 0; j < 8; j++) + sum += apv_trans_matrix[j][i] * scaled_coeff[j][x]; + tmp[i][x] = sum; + } + } + + // Renormalise. + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) + tmp[y][x] = (tmp[y][x] + 64) >> 7; + } + + // Horizontal transform of rows. + for (int y = 0; y < 8; y++) { + for (int i = 0; i < 8; i++) { + int sum = 0; + for (int j = 0; j < 8; j++) + sum += apv_trans_matrix[j][i] * tmp[y][j]; + recon_sample[y][i] = sum; + } + } + } + + // Output. + if (bit_depth == 8) { + uint8_t *ptr = output; + int bd_shift = 20 - bit_depth; + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + int sample = ((recon_sample[y][x] + + (1 << (bd_shift - 1))) >> bd_shift) + + (1 << (bit_depth - 1)); + ptr[x] = av_clip_uintp2(sample, bit_depth); + } + ptr += pitch; + } + } else { + uint16_t *ptr = output; + int bd_shift = 20 - bit_depth; + pitch /= 2; // Pitch was in bytes, 2 bytes per sample. + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + int sample = ((recon_sample[y][x] + + (1 << (bd_shift - 1))) >> bd_shift) + + (1 << (bit_depth - 1)); + ptr[x] = av_clip_uintp2(sample, bit_depth); + } + ptr += pitch; + } + } +} + +av_cold void ff_apv_dsp_init(APVDSPContext *dsp) +{ + dsp->decode_transquant = apv_decode_transquant_c; +} diff --git a/libavcodec/apv_dsp.h b/libavcodec/apv_dsp.h new file mode 100644 index 0000000000..31645b8581 --- /dev/null +++ b/libavcodec/apv_dsp.h @@ -0,0 +1,37 @@ +/* + * 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 + */ + +#ifndef AVCODEC_APV_DSP_H +#define AVCODEC_APV_DSP_H + +#include <stddef.h> +#include <stdint.h> + + +typedef struct APVDSPContext { + void (*decode_transquant)(void *output, + ptrdiff_t pitch, + const int16_t *input, + const int16_t *qmatrix, + int bit_depth, + int qp_shift); +} APVDSPContext; + +void ff_apv_dsp_init(APVDSPContext *dsp); + +#endif /* AVCODEC_APV_DSP_H */ diff --git a/libavcodec/apv_entropy.c b/libavcodec/apv_entropy.c new file mode 100644 index 0000000000..00e0b4fbdf --- /dev/null +++ b/libavcodec/apv_entropy.c @@ -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; +} -- 2.47.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".