| /* |
| * Copyright (C) 2007 Marco Gerards <marco@gnu.org> |
| * Copyright (C) 2009 David Conrad |
| * |
| * 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 |
| */ |
| |
| /** |
| * @file |
| * Arithmetic decoder for Dirac |
| * @author Marco Gerards <marco@gnu.org> |
| */ |
| |
| #ifndef AVCODEC_DIRAC_ARITH_H |
| #define AVCODEC_DIRAC_ARITH_H |
| |
| #include "libavutil/x86/asm.h" |
| #include "bytestream.h" |
| #include "get_bits.h" |
| |
| enum dirac_arith_contexts { |
| CTX_ZPZN_F1, |
| CTX_ZPNN_F1, |
| CTX_NPZN_F1, |
| CTX_NPNN_F1, |
| CTX_ZP_F2, |
| CTX_ZP_F3, |
| CTX_ZP_F4, |
| CTX_ZP_F5, |
| CTX_ZP_F6, |
| CTX_NP_F2, |
| CTX_NP_F3, |
| CTX_NP_F4, |
| CTX_NP_F5, |
| CTX_NP_F6, |
| CTX_COEFF_DATA, |
| CTX_SIGN_NEG, |
| CTX_SIGN_ZERO, |
| CTX_SIGN_POS, |
| CTX_ZERO_BLOCK, |
| CTX_DELTA_Q_F, |
| CTX_DELTA_Q_DATA, |
| CTX_DELTA_Q_SIGN, |
| |
| DIRAC_CTX_COUNT |
| }; |
| |
| // Dirac resets the arith decoder between decoding various types of data, |
| // so many contexts are never used simultaneously. Thus, we can reduce |
| // the number of contexts needed by reusing them. |
| #define CTX_SB_F1 CTX_ZP_F5 |
| #define CTX_SB_DATA 0 |
| #define CTX_PMODE_REF1 0 |
| #define CTX_PMODE_REF2 1 |
| #define CTX_GLOBAL_BLOCK 2 |
| #define CTX_MV_F1 CTX_ZP_F2 |
| #define CTX_MV_DATA 0 |
| #define CTX_DC_F1 CTX_ZP_F5 |
| #define CTX_DC_DATA 0 |
| |
| typedef struct { |
| unsigned low; |
| uint16_t range; |
| int16_t counter; |
| |
| const uint8_t *bytestream; |
| const uint8_t *bytestream_end; |
| |
| uint16_t contexts[DIRAC_CTX_COUNT]; |
| int error; |
| int overread; |
| } DiracArith; |
| |
| extern const uint8_t ff_dirac_next_ctx[DIRAC_CTX_COUNT]; |
| extern int16_t ff_dirac_prob_branchless[256][2]; |
| |
| static inline void renorm(DiracArith *c) |
| { |
| #if HAVE_FAST_CLZ |
| int shift = 14 - av_log2_16bit(c->range-1) + ((c->range-1)>>15); |
| |
| c->low <<= shift; |
| c->range <<= shift; |
| c->counter += shift; |
| #else |
| while (c->range <= 0x4000) { |
| c->low <<= 1; |
| c->range <<= 1; |
| c->counter++; |
| } |
| #endif |
| } |
| |
| static inline void refill(DiracArith *c) |
| { |
| int counter = c->counter; |
| |
| if (counter >= 0) { |
| int new = bytestream_get_be16(&c->bytestream); |
| |
| // the spec defines overread bits to be 1, and streams rely on this |
| if (c->bytestream > c->bytestream_end) { |
| new |= 0xff; |
| if (c->bytestream > c->bytestream_end+1) |
| new |= 0xff00; |
| |
| c->bytestream = c->bytestream_end; |
| c->overread ++; |
| if (c->overread > 4) |
| c->error = AVERROR_INVALIDDATA; |
| } |
| |
| c->low += new << counter; |
| counter -= 16; |
| } |
| c->counter = counter; |
| } |
| |
| static inline int dirac_get_arith_bit(DiracArith *c, int ctx) |
| { |
| int prob_zero = c->contexts[ctx]; |
| int range_times_prob, bit; |
| unsigned low = c->low; |
| int range = c->range; |
| |
| range_times_prob = (c->range * prob_zero) >> 16; |
| |
| #if ARCH_X86 && HAVE_FAST_CMOV && HAVE_INLINE_ASM && HAVE_6REGS |
| low -= range_times_prob << 16; |
| range -= range_times_prob; |
| bit = 0; |
| __asm__( |
| "cmpl %5, %4 \n\t" |
| "setae %b0 \n\t" |
| "cmovb %3, %2 \n\t" |
| "cmovb %5, %1 \n\t" |
| : "+q"(bit), "+r"(range), "+r"(low) |
| : "r"(c->low), "r"(c->low>>16), |
| "r"(range_times_prob) |
| ); |
| #else |
| bit = (low >> 16) >= range_times_prob; |
| if (bit) { |
| low -= range_times_prob << 16; |
| range -= range_times_prob; |
| } else { |
| range = range_times_prob; |
| } |
| #endif |
| |
| c->contexts[ctx] += ff_dirac_prob_branchless[prob_zero>>8][bit]; |
| c->low = low; |
| c->range = range; |
| |
| renorm(c); |
| refill(c); |
| return bit; |
| } |
| |
| static inline int dirac_get_arith_uint(DiracArith *c, int follow_ctx, int data_ctx) |
| { |
| int ret = 1; |
| while (!dirac_get_arith_bit(c, follow_ctx)) { |
| if (ret >= 0x40000000) { |
| av_log(NULL, AV_LOG_ERROR, "dirac_get_arith_uint overflow\n"); |
| c->error = AVERROR_INVALIDDATA; |
| return -1; |
| } |
| ret <<= 1; |
| ret += dirac_get_arith_bit(c, data_ctx); |
| follow_ctx = ff_dirac_next_ctx[follow_ctx]; |
| } |
| return ret-1; |
| } |
| |
| static inline int dirac_get_arith_int(DiracArith *c, int follow_ctx, int data_ctx) |
| { |
| int ret = dirac_get_arith_uint(c, follow_ctx, data_ctx); |
| if (ret && dirac_get_arith_bit(c, data_ctx+1)) |
| ret = -ret; |
| return ret; |
| } |
| |
| void ff_dirac_init_arith_tables(void); |
| void ff_dirac_init_arith_decoder(DiracArith *c, GetBitContext *gb, int length); |
| |
| #endif /* AVCODEC_DIRAC_ARITH_H */ |