Skip to content
Snippets Groups Projects
CABACWriter.cpp 86.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* 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.
    */
    
    /** \file     CABACWriter.cpp
     *  \brief    Writer for low level syntax
     */
    
    #include "CommonLib/Contexts.h"
    #include "CABACWriter.h"
    
    #include "EncLib.h"
    
    #include "CommonLib/UnitTools.h"
    #include "CommonLib/dtrace_buffer.h"
    #include "CommonLib/BinaryDecisionTree.h"
    
    #include <map>
    #include <algorithm>
    #include <limits>
    
    
    //! \ingroup EncoderLib
    //! \{
    
    void CABACWriter::initCtxModels( const Slice& slice )
    {
      int       qp                = slice.getSliceQp();
      SliceType sliceType         = slice.getSliceType();
      SliceType encCABACTableIdx  = slice.getEncCABACTableIdx();
      if( !slice.isIntra() && (encCABACTableIdx==B_SLICE || encCABACTableIdx==P_SLICE) && slice.getPPS()->getCabacInitPresentFlag() )
      {
        sliceType = encCABACTableIdx;
      }
      m_BinEncoder.reset( qp, (int)sliceType );
    }
    
    
    
    template <class BinProbModel>
    SliceType xGetCtxInitId( const Slice& slice, const BinEncIf& binEncoder, Ctx& ctxTest )
    {
      const CtxStore<BinProbModel>& ctxStoreTest = static_cast<const CtxStore<BinProbModel>&>( ctxTest );
      const CtxStore<BinProbModel>& ctxStoreRef  = static_cast<const CtxStore<BinProbModel>&>( binEncoder.getCtx() );
      int qp = slice.getSliceQp();
      if( !slice.isIntra() )
      {
        SliceType aSliceTypeChoices[] = { B_SLICE, P_SLICE };
        uint64_t  bestCost            = std::numeric_limits<uint64_t>::max();
        SliceType bestSliceType       = aSliceTypeChoices[0];
        for (uint32_t idx=0; idx<2; idx++)
        {
          uint64_t  curCost           = 0;
          SliceType curSliceType      = aSliceTypeChoices[idx];
          ctxTest.init( qp, (int)curSliceType );
          for( int k = 0; k < Ctx::NumberOfContexts; k++ )
          {
            if( binEncoder.getNumBins(k) > 0 )
            {
              curCost += uint64_t( binEncoder.getNumBins(k) ) * ctxStoreRef[k].estFracExcessBits( ctxStoreTest[k] );
            }
          }
          if (curCost < bestCost)
          {
            bestSliceType = curSliceType;
            bestCost      = curCost;
          }
        }
        return bestSliceType;
      }
      else
      {
        return I_SLICE;
      }
    }
    
    
    SliceType CABACWriter::getCtxInitId( const Slice& slice )
    {
      switch( m_TestCtx.getBPMType() )
      {
      case BPM_Std:   return  xGetCtxInitId<BinProbModel_Std>   ( slice, m_BinEncoder, m_TestCtx );
      default:        return  NUMBER_OF_SLICE_TYPES;
      }
    }
    
    
    
    unsigned estBits( BinEncIf& binEnc, const std::vector<bool>& bins, const Ctx& ctx, const int ctxId, const uint8_t winSize )
    {
      binEnc.initCtxAndWinSize( ctxId, ctx, winSize );
      binEnc.start();
      const std::size_t numBins   = bins.size();
      unsigned          startBits = binEnc.getNumWrittenBits();
      for( std::size_t binId = 0; binId < numBins; binId++ )
      {
        unsigned  bin = ( bins[binId] ? 1 : 0 );
        binEnc.encodeBin( bin, ctxId );
      }
      unsigned endBits    = binEnc.getNumWrittenBits();
      unsigned codedBits  = endBits - startBits;
      return   codedBits;
    }
    
    
    
    
    
    //================================================================================
    //  clause 7.3.8.1
    //--------------------------------------------------------------------------------
    //    void  end_of_slice()
    //================================================================================
    
    void CABACWriter::end_of_slice()
    {
      m_BinEncoder.encodeBinTrm ( 1 );
      m_BinEncoder.finish       ();
    }
    
    
    
    
    //================================================================================
    //  clause 7.3.8.2
    //--------------------------------------------------------------------------------
    //    bool  coding_tree_unit( cs, area, qp, ctuRsAddr, skipSao )
    //================================================================================
    
    void CABACWriter::coding_tree_unit( CodingStructure& cs, const UnitArea& area, int (&qps)[2], unsigned ctuRsAddr, bool skipSao /* = false */ )
    {
      CUCtx cuCtx( qps[CH_L] );
      Partitioner *partitioner = PartitionerFactory::get( *cs.slice );
    
      partitioner->initCtu( area, CH_L, *cs.slice );
    
      if( !skipSao )
      {
        sao( *cs.slice, ctuRsAddr );
      }
    
      for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ )
      {
        codeAlfCtuEnableFlag( cs, ctuRsAddr, compIdx );
      }
    
    
      if ( CS::isDualITree(cs) && cs.pcv->chrFormat != CHROMA_400 && cs.pcv->maxCUWidth > 64 )
    
      {
        CUCtx chromaCuCtx(qps[CH_C]);
        Partitioner *chromaPartitioner = PartitionerFactory::get(*cs.slice);
        chromaPartitioner->initCtu(area, CH_C, *cs.slice);
        coding_tree(cs, *partitioner, cuCtx, chromaPartitioner, &chromaCuCtx);
        qps[CH_L] = cuCtx.qp;
        qps[CH_C] = chromaCuCtx.qp;
    
        delete chromaPartitioner;
      }
      else
      {
    
        coding_tree( cs, *partitioner, cuCtx );
        qps[CH_L] = cuCtx.qp;
        if( CS::isDualITree( cs ) && cs.pcv->chrFormat != CHROMA_400 )
        {
          CUCtx cuCtxChroma( qps[CH_C] );
          partitioner->initCtu( area, CH_C, *cs.slice );
          coding_tree( cs, *partitioner, cuCtxChroma );
          qps[CH_C] = cuCtxChroma.qp;
        }
    
    
      delete partitioner;
    }
    
    
    
    
    
    //================================================================================
    //  clause 7.3.8.3
    //--------------------------------------------------------------------------------
    //    void  sao             ( slice, ctuRsAddr )
    //    void  sao_block_pars  ( saoPars, bitDepths, sliceEnabled, leftMergeAvail, aboveMergeAvail, onlyEstMergeInfo )
    //    void  sao_offset_pars ( ctbPars, compID, sliceEnabled, bitDepth )
    //================================================================================
    
    void CABACWriter::sao( const Slice& slice, unsigned ctuRsAddr )
    {
      const SPS& sps = *slice.getSPS();
    
      if( !sps.getSAOEnabledFlag() )
    
      {
        return;
      }
    
      CodingStructure&     cs                     = *slice.getPic()->cs;
      const PreCalcValues& pcv                    = *cs.pcv;
      const SAOBlkParam&  sao_ctu_pars            = cs.picture->getSAO()[ctuRsAddr];
      bool                slice_sao_luma_flag     = ( slice.getSaoEnabledFlag( CHANNEL_TYPE_LUMA ) );
      bool                slice_sao_chroma_flag   = ( slice.getSaoEnabledFlag( CHANNEL_TYPE_CHROMA ) && sps.getChromaFormatIdc() != CHROMA_400 );
      if( !slice_sao_luma_flag && !slice_sao_chroma_flag )
      {
        return;
      }
    
      bool                sliceEnabled[3]         = { slice_sao_luma_flag, slice_sao_chroma_flag, slice_sao_chroma_flag };
      int                 frame_width_in_ctus     = pcv.widthInCtus;
      int                 ry                      = ctuRsAddr      / frame_width_in_ctus;
      int                 rx                      = ctuRsAddr - ry * frame_width_in_ctus;
      const Position      pos                     ( rx * cs.pcv->maxCUWidth, ry * cs.pcv->maxCUHeight );
      const unsigned      curSliceIdx             = slice.getIndependentSliceIdx();
    #if HEVC_TILES_WPP
      const unsigned      curTileIdx              = cs.picture->tileMap->getTileIdxMap( pos );
      bool                leftMergeAvail          = cs.getCURestricted( pos.offset( -(int)pcv.maxCUWidth, 0  ), curSliceIdx, curTileIdx, CH_L ) ? true : false;
      bool                aboveMergeAvail         = cs.getCURestricted( pos.offset( 0, -(int)pcv.maxCUHeight ), curSliceIdx, curTileIdx, CH_L ) ? true : false;
    #else
      bool                leftMergeAvail          = cs.getCURestricted( pos.offset( -(int)pcv.maxCUWidth, 0  ), curSliceIdx, CH_L ) ? true : false;
      bool                aboveMergeAvail         = cs.getCURestricted( pos.offset( 0, -(int)pcv.maxCUHeight ), curSliceIdx, CH_L ) ? true : false;
    #endif
      sao_block_pars( sao_ctu_pars, sps.getBitDepths(), sliceEnabled, leftMergeAvail, aboveMergeAvail, false );
    }
    
    
    void CABACWriter::sao_block_pars( const SAOBlkParam& saoPars, const BitDepths& bitDepths, bool* sliceEnabled, bool leftMergeAvail, bool aboveMergeAvail, bool onlyEstMergeInfo )
    {
      bool isLeftMerge  = false;
      bool isAboveMerge = false;
      if( leftMergeAvail )
      {
        // sao_merge_left_flag
        isLeftMerge   = ( saoPars[COMPONENT_Y].modeIdc == SAO_MODE_MERGE && saoPars[COMPONENT_Y].typeIdc == SAO_MERGE_LEFT );
        m_BinEncoder.encodeBin( (isLeftMerge), Ctx::SaoMergeFlag() );
      }
      if( aboveMergeAvail && !isLeftMerge )
      {
        // sao_merge_above_flag
        isAboveMerge  = ( saoPars[COMPONENT_Y].modeIdc == SAO_MODE_MERGE && saoPars[COMPONENT_Y].typeIdc == SAO_MERGE_ABOVE );
        m_BinEncoder.encodeBin( (isAboveMerge), Ctx::SaoMergeFlag() );
      }
      if( onlyEstMergeInfo )
      {
        return; //only for RDO
      }
      if( !isLeftMerge && !isAboveMerge )
      {
        // explicit parameters
        for( int compIdx=0; compIdx < MAX_NUM_COMPONENT; compIdx++ )
        {
          sao_offset_pars( saoPars[compIdx], ComponentID(compIdx), sliceEnabled[compIdx], bitDepths.recon[ toChannelType(ComponentID(compIdx)) ] );
        }
      }
    }
    
    
    void CABACWriter::sao_offset_pars( const SAOOffset& ctbPars, ComponentID compID, bool sliceEnabled, int bitDepth )
    {
      if( !sliceEnabled )
      {
        CHECK( ctbPars.modeIdc != SAO_MODE_OFF, "Sao must be off, if it is disabled on slice level" );
        return;
      }
      const bool isFirstCompOfChType = ( getFirstComponentOfChannel( toChannelType(compID) ) == compID );
    
      if( isFirstCompOfChType )
      {
        // sao_type_idx_luma / sao_type_idx_chroma
        if( ctbPars.modeIdc == SAO_MODE_OFF )
        {
          m_BinEncoder.encodeBin  ( 0, Ctx::SaoTypeIdx() );
        }
        else if( ctbPars.typeIdc == SAO_TYPE_BO )
        {
          m_BinEncoder.encodeBin  ( 1, Ctx::SaoTypeIdx() );
          m_BinEncoder.encodeBinEP( 0 );
        }
        else
        {
          CHECK(!( ctbPars.typeIdc < SAO_TYPE_START_BO ), "Unspecified error");
          m_BinEncoder.encodeBin  ( 1, Ctx::SaoTypeIdx() );
          m_BinEncoder.encodeBinEP( 1 );
        }
      }
    
      if( ctbPars.modeIdc == SAO_MODE_NEW )
      {
        const int maxOffsetQVal = SampleAdaptiveOffset::getMaxOffsetQVal( bitDepth );
        int       numClasses    = ( ctbPars.typeIdc == SAO_TYPE_BO ? 4 : NUM_SAO_EO_CLASSES );
        int       k             = 0;
        int       offset[4];
        for( int i = 0; i < numClasses; i++ )
        {
          if( ctbPars.typeIdc != SAO_TYPE_BO && i == SAO_CLASS_EO_PLAIN )
          {
            continue;
          }
          int classIdx = ( ctbPars.typeIdc == SAO_TYPE_BO ? ( ctbPars.typeAuxInfo + i ) % NUM_SAO_BO_CLASSES : i );
          offset[k++]  = ctbPars.offset[classIdx];
        }
    
        // sao_offset_abs
        for( int i = 0; i < 4; i++ )
        {
          unsigned absOffset = ( offset[i] < 0 ? -offset[i] : offset[i] );
          unary_max_eqprob( absOffset, maxOffsetQVal );
        }
    
        // band offset mode
        if( ctbPars.typeIdc == SAO_TYPE_BO )
        {
          // sao_offset_sign
          for( int i = 0; i < 4; i++ )
          {
            if( offset[i] )
            {
              m_BinEncoder.encodeBinEP( (offset[i] < 0) );
            }
          }
          // sao_band_position
          m_BinEncoder.encodeBinsEP( ctbPars.typeAuxInfo, NUM_SAO_BO_CLASSES_LOG2 );
        }
        // edge offset mode
        else
        {
          if( isFirstCompOfChType )
          {
            // sao_eo_class_luma / sao_eo_class_chroma
            CHECK( ctbPars.typeIdc - SAO_TYPE_START_EO < 0, "sao edge offset class is outside valid range" );
            m_BinEncoder.encodeBinsEP( ctbPars.typeIdc - SAO_TYPE_START_EO, NUM_SAO_EO_TYPES_LOG2 );
          }
        }
      }
    }
    
    
    
    //================================================================================
    //  clause 7.3.8.4
    //--------------------------------------------------------------------------------
    //    void  coding_tree       ( cs, partitioner, cuCtx )
    //    void  split_cu_flag     ( split, cs, partitioner )
    //    void  split_cu_mode_mt  ( split, cs, partitioner )
    //================================================================================
    
    void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitioner, CUCtx& cuCtx, Partitioner* pPartitionerChroma, CUCtx* pCuCtxChroma)
    {
      const PPS      &pps         = *cs.pps;
      const UnitArea &currArea    = partitioner.currArea();
      const CodingUnit &cu        = *cs.getCU( currArea.blocks[partitioner.chType], partitioner.chType );
    
      // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
      if( pps.getUseDQP() && partitioner.currDepth <= pps.getMaxCuDQPDepth() )
      {
        cuCtx.isDQPCoded          = false;
      }
      if( cs.slice->getUseChromaQpAdj() && partitioner.currDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth() )
      {
        cuCtx.isChromaQpAdjCoded  = false;
      }
      // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
      if (CS::isDualITree(cs) && pPartitionerChroma != nullptr)
      {
        if (pps.getUseDQP() && pPartitionerChroma->currDepth <= pps.getMaxCuDQPDepth())
        {
          pCuCtxChroma->isDQPCoded = false;
        }
        if (cs.slice->getUseChromaQpAdj() && pPartitionerChroma->currDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth())
        {
          pCuCtxChroma->isChromaQpAdjCoded = false;
        }
      }
    
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #if JVET_M0421_SPLIT_SIG
      const PartSplit splitMode = CU::getSplitAtDepth( cu, partitioner.currDepth );
    
      split_cu_mode( splitMode, cs, partitioner );
    
      CHECK( !partitioner.canSplit( splitMode, cs ), "The chosen split mode is invalid!" );
    
      if( splitMode != CU_DONT_SPLIT )
      {
    #else
    
      const PartSplit implicitSplit = partitioner.getImplicitSplit( cs );
    
      // QT
      bool canQtSplit = partitioner.canSplit( CU_QUAD_SPLIT, cs );
    
      if( canQtSplit )
      {
        // split_cu_flag
        bool qtSplit = implicitSplit == CU_QUAD_SPLIT;
    
        if( !qtSplit && implicitSplit != CU_QUAD_SPLIT )
        {
          qtSplit = ( cu.qtDepth > partitioner.currQtDepth );
          split_cu_flag( qtSplit, cs, partitioner );
        }
    
        // quad-tree split
        if( qtSplit )
        {
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #endif
    
          if (CS::isDualITree(cs) && pPartitionerChroma != nullptr && (partitioner.currArea().lwidth() >= 64 || partitioner.currArea().lheight() >= 64))
          {
            partitioner.splitCurrArea(CU_QUAD_SPLIT, cs);
            pPartitionerChroma->splitCurrArea(CU_QUAD_SPLIT, cs);
            bool beContinue = true;
            bool lumaContinue = true;
            bool chromaContinue = true;
    
            while (beContinue)
            {
              if (partitioner.currArea().lwidth() > 64 || partitioner.currArea().lheight() > 64)
              {
                if (cs.picture->blocks[partitioner.chType].contains(partitioner.currArea().blocks[partitioner.chType].pos()))
                {
                  coding_tree(cs, partitioner, cuCtx, pPartitionerChroma, pCuCtxChroma);
                }
                lumaContinue = partitioner.nextPart(cs);
                chromaContinue = pPartitionerChroma->nextPart(cs);
                CHECK(lumaContinue != chromaContinue, "luma chroma partition should be matched");
                beContinue = lumaContinue;
              }
              else
              {
                //dual tree coding under 64x64 block
                if (cs.picture->blocks[partitioner.chType].contains(partitioner.currArea().blocks[partitioner.chType].pos()))
                {
                  coding_tree(cs, partitioner, cuCtx);
                }
                lumaContinue = partitioner.nextPart(cs);
                if (cs.picture->blocks[pPartitionerChroma->chType].contains(pPartitionerChroma->currArea().blocks[pPartitionerChroma->chType].pos()))
                {
                  coding_tree(cs, *pPartitionerChroma, *pCuCtxChroma);
                }
                chromaContinue = pPartitionerChroma->nextPart(cs);
                CHECK(lumaContinue != chromaContinue, "luma chroma partition should be matched");
                beContinue = lumaContinue;
              }
            }
            partitioner.exitCurrSplit();
            pPartitionerChroma->exitCurrSplit();
    
          }
          else
          {
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #if JVET_M0421_SPLIT_SIG
          partitioner.splitCurrArea( splitMode, cs );
    #else
    
          partitioner.splitCurrArea( CU_QUAD_SPLIT, cs );
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #endif
    
    
          do
          {
            if( cs.picture->blocks[partitioner.chType].contains( partitioner.currArea().blocks[partitioner.chType].pos() ) )
            {
              coding_tree( cs, partitioner, cuCtx );
            }
          } while( partitioner.nextPart( cs ) );
    
          partitioner.exitCurrSplit();
          }
          return;
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #if !JVET_M0421_SPLIT_SIG
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #endif
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #if !JVET_M0421_SPLIT_SIG
    
      {
        bool mtSplit = partitioner.canSplit( CU_MT_SPLIT, cs );
    
        if( mtSplit )
        {
          const PartSplit splitMode = CU::getSplitAtDepth( cu, partitioner.currDepth );
    
          split_cu_mode_mt( splitMode, cs, partitioner );
    
          if( splitMode != CU_DONT_SPLIT )
          {
            partitioner.splitCurrArea( splitMode, cs );
            do
            {
              if( cs.picture->blocks[partitioner.chType].contains( partitioner.currArea().blocks[partitioner.chType].pos() ) )
              {
                coding_tree( cs, partitioner, cuCtx );
              }
            } while( partitioner.nextPart( cs ) );
    
            partitioner.exitCurrSplit();
            return;
          }
        }
      }
    
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #endif
    
      // Predict QP on start of quantization group
    
      if( pps.getUseDQP() && !cuCtx.isDQPCoded && CU::isQGStart( cu, partitioner ) )
    
      {
        cuCtx.qp = CU::predictQP( cu, cuCtx.qp );
      }
    
    
      // coding unit
      coding_unit( cu, partitioner, cuCtx );
    
      DTRACE_COND( ( isEncoding() ), g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width, cu.Y().height, cu.qp );
      DTRACE_BLOCK_REC_COND( ( !isEncoding() ), cs.picture->getRecoBuf( cu ), cu, cu.predMode );
    }
    
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #if JVET_M0421_SPLIT_SIG
    void CABACWriter::split_cu_mode( const PartSplit split, const CodingStructure& cs, Partitioner& partitioner )
    {
      bool canNo, canQt, canBh, canBv, canTh, canTv;
      partitioner.canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv );
    
      bool canSpl[6] = { canNo, canQt, canBh, canBv, canTh, canTv };
    
      unsigned ctxSplit = 0, ctxQtSplit = 0, ctxBttHV = 0, ctxBttH12 = 0, ctxBttV12;
      DeriveCtx::CtxSplit( cs, partitioner, ctxSplit, ctxQtSplit, ctxBttHV, ctxBttH12, ctxBttV12, canSpl );
      
      const bool canSplit = canBh || canBv || canTh || canTv || canQt;
      const bool isNo     = split == CU_DONT_SPLIT;
    
      if( canNo && canSplit )
      {
        m_BinEncoder.encodeBin( !isNo, Ctx::SplitFlag( ctxSplit ) );
      }
    
      DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d split=%d\n", ctxSplit, !isNo );
    
      if( isNo )
      {
        return;
      }
    
      const bool canBtt = canBh || canBv || canTh || canTv;
      const bool isQt   = split == CU_QUAD_SPLIT;
    
      if( canQt && canBtt )
      {
        m_BinEncoder.encodeBin( isQt, Ctx::SplitQtFlag( ctxQtSplit ) );
      }
    
      DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d qt=%d\n", ctxQtSplit, isQt );
    
      if( isQt )
      {
        return;
      }
    
      const bool canHor = canBh || canTh;
      const bool canVer = canBv || canTv;
      const bool  isVer = split == CU_VERT_SPLIT || split == CU_TRIV_SPLIT;
    
      if( canVer && canHor )
      {
        m_BinEncoder.encodeBin( isVer, Ctx::SplitHvFlag( ctxBttHV ) );
      }
    
      const bool can14 = isVer ? canTv : canTh;
      const bool can12 = isVer ? canBv : canBh;
      const bool  is12 = isVer ? ( split == CU_VERT_SPLIT ) : ( split == CU_HORZ_SPLIT );
    
      if( can12 && can14 )
      {
        m_BinEncoder.encodeBin( is12, Ctx::Split12Flag( isVer ? ctxBttV12 : ctxBttH12 ) );
      }
    
      DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctxHv=%d ctx12=%d mode=%d\n", ctxBttHV, isVer ? ctxBttV12 : ctxBttH12, split );
    }
    #else
    
    void CABACWriter::split_cu_flag( bool split, const CodingStructure& cs, Partitioner& partitioner )
    {
    
      unsigned maxQTDepth = ( g_aucLog2[cs.sps->getCTUSize()] - g_aucLog2[cs.sps->getMinQTSize(cs.slice->getSliceType(), partitioner.chType)] );
    
      if( partitioner.currDepth == maxQTDepth )
      {
        return;
      }
      unsigned  ctxId = DeriveCtx::CtxCUsplit( cs, partitioner );
      m_BinEncoder.encodeBin( (split), Ctx::SplitFlag(ctxId) );
    
      DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_flag() ctx=%d split=%d\n", ctxId, split ? 1 : 0 );
    }
    
    void CABACWriter::split_cu_mode_mt(const PartSplit split, const CodingStructure& cs, Partitioner& partitioner)
    {
      unsigned ctxIdBT = DeriveCtx::CtxBTsplit( cs, partitioner );
    
      unsigned width   = partitioner.currArea().lumaSize().width;
      unsigned height  = partitioner.currArea().lumaSize().height;
    
    
    #if REMOVE_BIN_DECISION_TREE
      unsigned btSCtxId = width == height ? 0 : ( width > height ? 1 : 2 );
    
      const bool canNo  = partitioner.canSplit( CU_DONT_SPLIT, cs );
      const bool canBh  = partitioner.canSplit( CU_HORZ_SPLIT, cs );
      const bool canBv  = partitioner.canSplit( CU_VERT_SPLIT, cs );
      const bool canTh  = partitioner.canSplit( CU_TRIH_SPLIT, cs );
      const bool canTv  = partitioner.canSplit( CU_TRIV_SPLIT, cs );
    
      const bool canSplit = canBh || canBv || canTh || canTv;
      const bool isNo     = split == CU_DONT_SPLIT;
    
      if( canNo && canSplit )
      {
        m_BinEncoder.encodeBin( !isNo, Ctx::BTSplitFlag( ctxIdBT ) );
      }
    
      if( isNo )
      {
        DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode_mt() ctx=%d split=%d\n", ctxIdBT, split );
    
        return;
      }
    
      const bool canHor = canBh || canTh;
      const bool canVer = canBv || canTv;
      const bool  isVer = split == CU_VERT_SPLIT || split == CU_TRIV_SPLIT;
    
      if( canVer && canHor )
      {
        m_BinEncoder.encodeBin( isVer, Ctx::BTSplitFlag( 12 + btSCtxId ) );
      }
    
      const bool can14 = isVer ? canTv : canTh;
      const bool can12 = isVer ? canBv : canBh;
      const bool  is12 = isVer ? ( split == CU_VERT_SPLIT ) : ( split == CU_HORZ_SPLIT );
    
    
      if( can12 && can14 )
      {
        m_BinEncoder.encodeBin( is12, Ctx::BTSplitFlag( 15 ) );
      }
    #else
    
      DecisionTree dt( g_mtSplitDTT );
    
      dt.setAvail( DTT_SPLIT_BT_HORZ,  partitioner.canSplit( CU_HORZ_SPLIT, cs ) );
      dt.setAvail( DTT_SPLIT_BT_VERT,  partitioner.canSplit( CU_VERT_SPLIT, cs ) );
    
      dt.setAvail( DTT_SPLIT_TT_HORZ,  partitioner.canSplit( CU_TRIH_SPLIT, cs ) );
      dt.setAvail( DTT_SPLIT_TT_VERT,  partitioner.canSplit( CU_TRIV_SPLIT, cs ) );
      dt.setAvail( DTT_SPLIT_NO_SPLIT, partitioner.canSplit( CU_DONT_SPLIT, cs ) );
    
      unsigned btSCtxId = width == height ? 0 : ( width > height ? 1 : 2 );
      dt.setCtxId( DTT_SPLIT_DO_SPLIT_DECISION,   Ctx::BTSplitFlag( ctxIdBT ) );
    
      dt.setCtxId( DTT_SPLIT_HV_DECISION,         Ctx::BTSplitFlag( 12 + btSCtxId ) );
    
      dt.setCtxId( DTT_SPLIT_H_IS_BT_12_DECISION, Ctx::BTSplitFlag( 15 ) );
      dt.setCtxId( DTT_SPLIT_V_IS_BT_12_DECISION, Ctx::BTSplitFlag( 15 ) );
    
    
    
      encode_sparse_dt( dt, split == CU_DONT_SPLIT ? ( unsigned ) DTT_SPLIT_NO_SPLIT : ( unsigned ) split );
    
    
      DTRACE(g_trace_ctx, D_SYNTAX, "split_cu_mode_mt() ctx=%d split=%d\n", ctxIdBT, split);
    }
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    #endif
    
    
    //================================================================================
    //  clause 7.3.8.5
    //--------------------------------------------------------------------------------
    //    void  coding_unit               ( cu, partitioner, cuCtx )
    //    void  cu_transquant_bypass_flag ( cu )
    //    void  cu_skip_flag              ( cu )
    //    void  pred_mode                 ( cu )
    //    void  part_mode                 ( cu )
    //    void  pcm_flag                  ( cu )
    //    void  pcm_samples               ( tu )
    //    void  cu_pred_data              ( pus )
    //    void  cu_lic_flag               ( cu )
    //    void  intra_luma_pred_modes     ( pus )
    //    void  intra_chroma_pred_mode    ( pu )
    //    void  cu_residual               ( cu, partitioner, cuCtx )
    //    void  rqt_root_cbf              ( cu )
    //    void  end_of_ctu                ( cu, cuCtx )
    //================================================================================
    
    void CABACWriter::coding_unit( const CodingUnit& cu, Partitioner& partitioner, CUCtx& cuCtx )
    {
      CodingStructure& cs = *cu.cs;
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
      cs.chType = partitioner.chType;
    
      // transquant bypass flag
      if( cs.pps->getTransquantBypassEnabledFlag() )
      {
        cu_transquant_bypass_flag( cu );
      }
    
      // skip flag
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
      if (!cs.slice->isIntra() && cu.Y().valid())
    
      {
        cu_skip_flag( cu );
      }
    
    
      // skip data
      if( cu.skip )
      {
        CHECK( !cu.firstPU->mergeFlag, "Merge flag has to be on!" );
        PredictionUnit&   pu = *cu.firstPU;
        prediction_unit ( pu );
        end_of_ctu      ( cu, cuCtx );
        return;
      }
    
    
      {
        pcm_data( cu, partitioner );
        if( cu.ipcm )
        {
          end_of_ctu( cu, cuCtx );
          return;
        }
      }
    
    
      // prediction mode and partitioning data
      pred_mode ( cu );
    
    
      extend_ref_line(cu);
    
    
    
      // prediction data ( intra prediction modes / reference indexes + motion vectors )
      cu_pred_data( cu );
    
      // residual data ( coded block flags + transform coefficient levels )
      cu_residual( cu, partitioner, cuCtx );
    
      // end of cu
      end_of_ctu( cu, cuCtx );
    }
    
    
    void CABACWriter::cu_transquant_bypass_flag( const CodingUnit& cu )
    {
      m_BinEncoder.encodeBin( (cu.transQuantBypass), Ctx::TransquantBypassFlag() );
    }
    
    
    void CABACWriter::cu_skip_flag( const CodingUnit& cu )
    {
      unsigned ctxId = DeriveCtx::CtxSkipFlag( cu );
      m_BinEncoder.encodeBin( ( cu.skip ), Ctx::SkipFlag( ctxId ) );
    
      DTRACE( g_trace_ctx, D_SYNTAX, "cu_skip_flag() ctx=%d skip=%d\n", ctxId, cu.skip ? 1 : 0 );
    
      if (cu.skip)
      {
        m_BinEncoder.encodeBin(cu.mmvdSkip, Ctx::MmvdFlag(0));
        DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_cu_skip_flag() ctx=%d mmvd_skip=%d\n", 0, cu.mmvdSkip ? 1 : 0);
      }
    
    }
    
    
    void CABACWriter::pred_mode( const CodingUnit& cu )
    {
      if( cu.cs->slice->isIntra() )
      {
        return;
      }
    
    Xin Zhao's avatar
    Xin Zhao committed
    #if JVET_M0502_PRED_MODE_CTX
      m_BinEncoder.encodeBin( ( CU::isIntra( cu ) ), Ctx::PredMode( DeriveCtx::CtxPredModeFlag( cu ) ) );
    #else
    
      m_BinEncoder.encodeBin( ( CU::isIntra( cu ) ), Ctx::PredMode() );
    
    Xin Zhao's avatar
    Xin Zhao committed
    #endif
    
    void CABACWriter::pcm_data( const CodingUnit& cu, Partitioner& partitioner  )
    
      pcm_flag( cu, partitioner );
    
      if( cu.ipcm )
      {
        m_BinEncoder.pcmAlignBits();
        pcm_samples( *cu.firstTU );
      }
    }
    
    
    void CABACWriter::pcm_flag( const CodingUnit& cu, Partitioner& partitioner )
    
      if( !sps.getPCMEnabledFlag() || partitioner.currArea().lwidth() > (1 << sps.getPCMLog2MaxSize()) || partitioner.currArea().lwidth() < (1 << sps.getPCMLog2MinSize()) 
    
          || partitioner.currArea().lheight() > (1 << sps.getPCMLog2MaxSize()) || partitioner.currArea().lheight() < (1 << sps.getPCMLog2MinSize()) )
    
      {
        return;
      }
      m_BinEncoder.encodeBinTrm( cu.ipcm );
    }
    
    
    void CABACWriter::cu_pred_data( const CodingUnit& cu )
    {
      if( CU::isIntra( cu ) )
      {
        intra_luma_pred_modes  ( cu );
        intra_chroma_pred_modes( cu );
        return;
      }
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
      if (!cu.Y().valid()) // dual tree chroma CU
      {
        return;
      }
    
      for( auto &pu : CU::traversePUs( cu ) )
      {
        prediction_unit( pu );
      }
    
      imv_mode   ( cu );
    
      cu_gbi_flag( cu );
    
    
    void CABACWriter::cu_gbi_flag(const CodingUnit& cu)
    {
      if(!CU::isGBiIdxCoded(cu))
      {
        return;
      }
    
      CHECK(!(GBI_NUM > 1 && (GBI_NUM == 2 || (GBI_NUM & 0x01) == 1)), " !( GBI_NUM > 1 && ( GBI_NUM == 2 || ( GBI_NUM & 0x01 ) == 1 ) ) ");
      const uint8_t gbiCodingIdx = (uint8_t)g_GbiCodingOrder[CU::getValidGbiIdx(cu)];
    
      int ctxId = 0;
    
      int32_t numGBi = (cu.slice->getCheckLDC()) ? 5 : 3;
    
      m_BinEncoder.encodeBin((gbiCodingIdx == 0 ? 1 : 0), Ctx::GBiIdx(ctxId));
    
      if(numGBi > 2 && gbiCodingIdx != 0)
      {
        uint32_t prefixNumBits = numGBi - 2;
        uint32_t step = 1;
        uint8_t prefixSymbol = gbiCodingIdx;
    
        int ctxIdGBi = 4;
        uint8_t idx = 1;
        for(int ui = 0; ui < prefixNumBits; ++ui)
        {
          if (prefixSymbol == idx)
          {
            m_BinEncoder.encodeBin(1, Ctx::GBiIdx(ctxIdGBi));
            break;
          }
          else
          {
            m_BinEncoder.encodeBin(0, Ctx::GBiIdx(ctxIdGBi));
            ctxIdGBi += step;
            idx += step;
          }
        }
      }
    
      DTRACE(g_trace_ctx, D_SYNTAX, "cu_gbi_flag() gbi_idx=%d\n", cu.GBiIdx ? 1 : 0);
    }
    
    ling's avatar
    ling committed
    void CABACWriter::xWriteTruncBinCode(uint32_t symbol, uint32_t maxSymbol)
    
    ling's avatar
    ling committed
      int thresh;
      if (maxSymbol > 256)
    
    ling's avatar
    ling committed
        int threshVal = 1 << 8;
        thresh = 8;
        while (threshVal <= maxSymbol)
    
    ling's avatar
    ling committed
          thresh++;
          threshVal <<= 1;
    
    ling's avatar
    ling committed
        thresh--;
    
    ling's avatar
    ling committed
        thresh = g_tbMax[maxSymbol];
    
    ling's avatar
    ling committed
      int val = 1 << thresh;
      assert(val <= maxSymbol);
      assert((val << 1) > maxSymbol);
      assert(symbol < maxSymbol);
      int b = maxSymbol - val;
      assert(b < val);
      if (symbol < val - b)
    
    ling's avatar
    ling committed
        m_BinEncoder.encodeBinsEP(symbol, thresh);
    
    ling's avatar
    ling committed
        symbol += val - b;
        assert(symbol < (val << 1));
        assert((symbol >> 1) >= val - b);
        m_BinEncoder.encodeBinsEP(symbol, thresh + 1);
    
    void CABACWriter::extend_ref_line(const PredictionUnit& pu)
    {
      const CodingUnit& cu = *pu.cu;
      if (!cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType))
      {
        return;
      }
      bool isFirstLineOfCtu = (((cu.block(COMPONENT_Y).y)&((cu.cs->sps)->getMaxCUWidth() - 1)) == 0);
      if (isFirstLineOfCtu)
      {
        return;
      }
      int multiRefIdx = pu.multiRefIdx;
      if (MRL_NUM_REF_LINES > 1)
      {
        m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[0], Ctx::MultiRefLineIdx(0));
        if (MRL_NUM_REF_LINES > 2 && multiRefIdx != MULTI_REF_LINE_IDX[0])
        {
          m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[1], Ctx::MultiRefLineIdx(1));
          if (MRL_NUM_REF_LINES > 3 && multiRefIdx != MULTI_REF_LINE_IDX[1])
          {
            m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[2], Ctx::MultiRefLineIdx(2));
          }
        }
      }
    }
    
    void CABACWriter::extend_ref_line(const CodingUnit& cu)
    {
      if (!cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType))
      {
        return;
      }
    
      const int numBlocks = CU::getNumPUs(cu);
      const PredictionUnit* pu = cu.firstPU;
    
      for (int k = 0; k < numBlocks; k++)
      {
        bool isFirstLineOfCtu = (((cu.block(COMPONENT_Y).y)&((cu.cs->sps)->getMaxCUWidth() - 1)) == 0);
        if (isFirstLineOfCtu)
        {
          return;
        }
        int multiRefIdx = pu->multiRefIdx;
        if (MRL_NUM_REF_LINES > 1)
        {
          m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[0], Ctx::MultiRefLineIdx(0));
          if (MRL_NUM_REF_LINES > 2 && multiRefIdx != MULTI_REF_LINE_IDX[0])
          {
            m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[1], Ctx::MultiRefLineIdx(1));
            if (MRL_NUM_REF_LINES > 3 && multiRefIdx != MULTI_REF_LINE_IDX[1])
            {
              m_BinEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[2], Ctx::MultiRefLineIdx(2));
            }
          }
    
        }
        pu = pu->next;
      }
    }
    
    
    void CABACWriter::intra_luma_pred_modes( const CodingUnit& cu )
    {
      if( !cu.Y().valid() )
      {
        return;
      }
    
    
      const int numMPMs   = NUM_MOST_PROBABLE_MODES;
      const int numBlocks = CU::getNumPUs( cu );
      unsigned  mpm_preds   [4][numMPMs];
      unsigned  mpm_idxs    [4];
      unsigned  ipred_modes [4];
    
    
      const PredictionUnit* pu = cu.firstPU;
    
      // prev_intra_luma_pred_flag
      for( int k = 0; k < numBlocks; k++ )
      {
    
        unsigned*  mpm_pred   = mpm_preds[k];