/* The copyright in this software is being made available under the BSD * License, included below. This software may be subject to other third party * and contributor rights, including patent rights, and no such rights are * granted under this license. * * Copyright (c) 2010-2019, ITU/ISO/IEC * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "BinEncoder.h" #include "CommonLib/Rom.h" #include "CommonLib/dtrace_next.h" BinCounter::BinCounter() : m_CtxBinsCodedBuffer( Ctx::NumberOfContexts ) , m_NumBinsCtx ( m_CtxBinsCodedBuffer.data() ) , m_NumBinsEP ( 0 ) , m_NumBinsTrm ( 0 ) {} void BinCounter::reset() { for( std::size_t k = 0; k < m_CtxBinsCodedBuffer.size(); k++ ) { m_NumBinsCtx[k] = 0; } m_NumBinsEP = 0; m_NumBinsTrm = 0; } uint32_t BinCounter::getAll() const { uint32_t count = m_NumBinsEP + m_NumBinsTrm; for( std::size_t k = 0; k < m_CtxBinsCodedBuffer.size(); k++ ) { count += m_NumBinsCtx[k]; } return count; } template <class BinProbModel> BinEncoderBase::BinEncoderBase( const BinProbModel* dummy ) : BinEncIf ( dummy ) , m_Bitstream ( 0 ) , m_Low ( 0 ) , m_Range ( 0 ) , m_bufferedByte ( 0 ) , m_numBufferedBytes( 0 ) , m_bitsLeft ( 0 ) {} void BinEncoderBase::init( OutputBitstream* bitstream ) { m_Bitstream = bitstream; } void BinEncoderBase::uninit() { m_Bitstream = 0; } void BinEncoderBase::start() { m_Low = 0; m_Range = 510; m_bufferedByte = 0xff; m_numBufferedBytes = 0; m_bitsLeft = 23; BinCounter::reset(); m_BinStore. reset(); } void BinEncoderBase::finish() { if( m_Low >> ( 32 - m_bitsLeft ) ) { m_Bitstream->write( m_bufferedByte + 1, 8 ); while( m_numBufferedBytes > 1 ) { m_Bitstream->write( 0x00, 8 ); m_numBufferedBytes--; } m_Low -= 1 << ( 32 - m_bitsLeft ); } else { if( m_numBufferedBytes > 0 ) { m_Bitstream->write( m_bufferedByte, 8 ); } while( m_numBufferedBytes > 1 ) { m_Bitstream->write( 0xff, 8 ); m_numBufferedBytes--; } } m_Bitstream->write( m_Low >> 8, 24 - m_bitsLeft ); } void BinEncoderBase::restart() { m_Low = 0; m_Range = 510; m_bufferedByte = 0xff; m_numBufferedBytes = 0; m_bitsLeft = 23; } void BinEncoderBase::reset( int qp, int initId ) { Ctx::init( qp, initId ); start(); } void BinEncoderBase::resetBits() { m_Low = 0; m_bufferedByte = 0xff; m_numBufferedBytes = 0; m_bitsLeft = 23; BinCounter::reset(); } void BinEncoderBase::encodeBinEP( unsigned bin ) { DTRACE( g_trace_ctx, D_CABAC, "%d" " " "%d" " EP=%d \n", DTRACE_GET_COUNTER( g_trace_ctx, D_CABAC ), m_Range, bin ); BinCounter::addEP(); m_Low <<= 1; if( bin ) { m_Low += m_Range; } m_bitsLeft--; if( m_bitsLeft < 12 ) { writeOut(); } } void BinEncoderBase::encodeBinsEP( unsigned bins, unsigned numBins ) { for(int i = 0; i < numBins; i++) { DTRACE( g_trace_ctx, D_CABAC, "%d" " " "%d" " EP=%d \n", DTRACE_GET_COUNTER( g_trace_ctx, D_CABAC ), m_Range, ( bins >> ( numBins - 1 - i ) ) & 1 ); } BinCounter::addEP( numBins ); if( m_Range == 256 ) { encodeAlignedBinsEP( bins, numBins ); return; } while( numBins > 8 ) { numBins -= 8; unsigned pattern = bins >> numBins; m_Low <<= 8; m_Low += m_Range * pattern; bins -= pattern << numBins; m_bitsLeft -= 8; if( m_bitsLeft < 12 ) { writeOut(); } } m_Low <<= numBins; m_Low += m_Range * bins; m_bitsLeft -= numBins; if( m_bitsLeft < 12 ) { writeOut(); } } void BinEncoderBase::encodeRemAbsEP( unsigned bins, unsigned goRicePar, bool useLimitedPrefixLength, int maxLog2TrDynamicRange ) { const unsigned threshold = COEF_REMAIN_BIN_REDUCTION << goRicePar; useLimitedPrefixLength = true; if( bins < threshold ) { const unsigned bitMask = ( 1 << goRicePar ) - 1; const unsigned length = ( bins >> goRicePar ) + 1; encodeBinsEP( ( 1 << length ) - 2, length ); encodeBinsEP( bins & bitMask, goRicePar); } else if (useLimitedPrefixLength) { const unsigned maxPrefixLength = 32 - COEF_REMAIN_BIN_REDUCTION - maxLog2TrDynamicRange; unsigned prefixLength = 0; unsigned codeValue = ( bins >> goRicePar ) - COEF_REMAIN_BIN_REDUCTION; unsigned suffixLength; if( codeValue >= ( ( 1 << maxPrefixLength ) - 1 ) ) { prefixLength = maxPrefixLength; suffixLength = maxLog2TrDynamicRange; } else { while( codeValue > ( ( 2 << prefixLength ) - 2 ) ) { prefixLength++; } suffixLength = prefixLength + goRicePar + 1; //+1 for the separator bit } const unsigned totalPrefixLength = prefixLength + COEF_REMAIN_BIN_REDUCTION; const unsigned bitMask = ( 1 << goRicePar ) - 1; const unsigned prefix = ( 1 << totalPrefixLength ) - 1; const unsigned suffix = ( ( codeValue - ( (1 << prefixLength ) - 1 ) ) << goRicePar ) | ( bins & bitMask ); encodeBinsEP( prefix, totalPrefixLength ); //prefix encodeBinsEP( suffix, suffixLength ); //separator, suffix, and rParam bits } else { unsigned length = goRicePar; unsigned delta = 1 << length; bins -= threshold; while (bins >= delta ) { bins -= delta; delta = 1 << (++length); } unsigned numBin = COEF_REMAIN_BIN_REDUCTION + length + 1 - goRicePar; encodeBinsEP( ( 1 << numBin ) - 2, numBin ); encodeBinsEP( bins, length ); } } void BinEncoderBase::encodeBinTrm( unsigned bin ) { BinCounter::addTrm(); m_Range -= 2; if( bin ) { m_Low += m_Range; m_Low <<= 7; m_Range = 2 << 7; m_bitsLeft -= 7; } else if( m_Range >= 256 ) { return; } else { m_Low <<= 1; m_Range <<= 1; m_bitsLeft--; } if( m_bitsLeft < 12 ) { writeOut(); } } void BinEncoderBase::encodeBinsPCM( unsigned bins, unsigned numBins ) { m_Bitstream->write( bins, numBins ); } void BinEncoderBase::align() { m_Range = 256; } void BinEncoderBase::pcmAlignBits() { finish(); m_Bitstream->write( 1, 1 ); m_Bitstream->writeAlignZero(); // pcm align zero } void BinEncoderBase::encodeAlignedBinsEP( unsigned bins, unsigned numBins ) { unsigned remBins = numBins; while( remBins > 0 ) { //The process of encoding an EP bin is the same as that of coding a normal //bin where the symbol ranges for 1 and 0 are both half the range: // // low = (low + range/2) << 1 (to encode a 1) // low = low << 1 (to encode a 0) // // i.e. // low = (low + (bin * range/2)) << 1 // // which is equivalent to: // // low = (low << 1) + (bin * range) // // this can be generalised for multiple bins, producing the following expression: // unsigned binsToCode = std::min<unsigned>( remBins, 8); //code bytes if able to take advantage of the system's byte-write function unsigned binMask = ( 1 << binsToCode ) - 1; unsigned newBins = ( bins >> ( remBins - binsToCode ) ) & binMask; m_Low = ( m_Low << binsToCode ) + ( newBins << 8 ); //range is known to be 256 remBins -= binsToCode; m_bitsLeft -= binsToCode; if( m_bitsLeft < 12 ) { writeOut(); } } } void BinEncoderBase::writeOut() { unsigned leadByte = m_Low >> ( 24 - m_bitsLeft ); m_bitsLeft += 8; m_Low &= 0xffffffffu >> m_bitsLeft; if( leadByte == 0xff ) { m_numBufferedBytes++; } else { if( m_numBufferedBytes > 0 ) { unsigned carry = leadByte >> 8; unsigned byte = m_bufferedByte + carry; m_bufferedByte = leadByte & 0xff; m_Bitstream->write( byte, 8 ); byte = ( 0xff + carry ) & 0xff; while( m_numBufferedBytes > 1 ) { m_Bitstream->write( byte, 8 ); m_numBufferedBytes--; } } else { m_numBufferedBytes = 1; m_bufferedByte = leadByte; } } } template <class BinProbModel> TBinEncoder<BinProbModel>::TBinEncoder() : BinEncoderBase( static_cast<const BinProbModel*> ( nullptr ) ) , m_Ctx ( static_cast<CtxStore<BinProbModel>&>( *this ) ) {} template <class BinProbModel> void TBinEncoder<BinProbModel>::encodeBin( unsigned bin, unsigned ctxId ) { BinCounter::addCtx( ctxId ); BinProbModel& rcProbModel = m_Ctx[ctxId]; uint32_t LPS = rcProbModel.getLPS( m_Range ); DTRACE( g_trace_ctx, D_CABAC, "%d" " %d " "%d" " " "[%d:%d]" " " "%2d(MPS=%d)" " " " - " "%d" "\n", DTRACE_GET_COUNTER( g_trace_ctx, D_CABAC ), ctxId, m_Range, m_Range - LPS, LPS, ( unsigned int ) ( rcProbModel.state() ), bin == rcProbModel.mps(), bin ); m_Range -= LPS; if( bin != rcProbModel.mps() ) { int numBits = rcProbModel.getRenormBitsLPS( LPS ); m_bitsLeft -= numBits; m_Low += m_Range; m_Low = m_Low << numBits; m_Range = LPS << numBits; if( m_bitsLeft < 12 ) { writeOut(); } } else { if( m_Range < 256 ) { int numBits = rcProbModel.getRenormBitsRange( m_Range ); m_bitsLeft -= numBits; m_Low <<= numBits; m_Range <<= numBits; if( m_bitsLeft < 12 ) { writeOut(); } } } rcProbModel.update( bin ); BinEncoderBase::m_BinStore.addBin( bin, ctxId ); } template <class BinProbModel> BinEncIf* TBinEncoder<BinProbModel>::getTestBinEncoder() const { BinEncIf* testBinEncoder = 0; if( m_BinStore.inUse() ) { testBinEncoder = new TBinEncoder<BinProbModel>(); } return testBinEncoder; } template <class BinProbModel> BitEstimatorBase::BitEstimatorBase( const BinProbModel* dummy ) : BinEncIf ( dummy ) { m_EstFracBits = 0; } void BitEstimatorBase::encodeRemAbsEP( unsigned bins, unsigned goRicePar, bool useLimitedPrefixLength, int maxLog2TrDynamicRange ) { const unsigned threshold = COEF_REMAIN_BIN_REDUCTION << goRicePar; useLimitedPrefixLength = true; if( bins < threshold ) { m_EstFracBits += BinProbModelBase::estFracBitsEP( ( bins >> goRicePar ) + 1 + goRicePar ); } else if (useLimitedPrefixLength) { const unsigned maxPrefixLength = 32 - COEF_REMAIN_BIN_REDUCTION - maxLog2TrDynamicRange; unsigned prefixLength = 0; unsigned codeValue = ( bins >> goRicePar ) - COEF_REMAIN_BIN_REDUCTION; unsigned suffixLength; if( codeValue >= ( ( 1 << maxPrefixLength ) - 1 ) ) { prefixLength = maxPrefixLength; suffixLength = maxLog2TrDynamicRange; } else { while( codeValue > ( ( 2 << prefixLength ) - 2 ) ) { prefixLength++; } suffixLength = prefixLength + goRicePar + 1; //+1 for the separator bit } m_EstFracBits += BinProbModelBase::estFracBitsEP( COEF_REMAIN_BIN_REDUCTION + prefixLength + suffixLength ); } else { unsigned length = goRicePar; unsigned delta = 1 << length; bins -= threshold; while (bins >= delta ) { bins -= delta; delta = 1 << (++length); } m_EstFracBits += BinProbModelBase::estFracBitsEP(COEF_REMAIN_BIN_REDUCTION + 1 + (length << 1) - goRicePar); } } void BitEstimatorBase::align() { static const uint64_t add = BinProbModelBase::estFracBitsEP() - 1; static const uint64_t mask = ~add; m_EstFracBits += add; m_EstFracBits &= mask; } void BitEstimatorBase::pcmAlignBits() { uint64_t numCurrBits = ( m_EstFracBits >> SCALE_BITS ); uint64_t filledBytes = ( numCurrBits + 8 ) >> 3; // including aligned_one_bit and aligned_zero_bits unsigned bitsToAdd = unsigned( ( filledBytes << 3 ) - numCurrBits ); m_EstFracBits += BinProbModelBase::estFracBitsEP( bitsToAdd ); } template <class BinProbModel> TBitEstimator<BinProbModel>::TBitEstimator() : BitEstimatorBase ( static_cast<const BinProbModel*> ( nullptr) ) , m_Ctx ( static_cast<CtxStore<BinProbModel>&>( *this ) ) {} template class TBinEncoder<BinProbModel_Std>; template class TBitEstimator<BinProbModel_Std>;