Skip to content
Snippets Groups Projects
CABACWriter.cpp 84.9 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 <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();
      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;
      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.currQgEnable() )
      {
        cuCtx.qgStart    = true;
        cuCtx.isDQPCoded          = false;
      }
      if( cs.slice->getUseChromaQpAdj() && partitioner.currQgChromaEnable() )
    
      {
        cuCtx.isChromaQpAdjCoded  = false;
      }
      // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
      if (CS::isDualITree(cs) && pPartitionerChroma != nullptr)
      {
    
        if (pps.getUseDQP() && pPartitionerChroma->currQgEnable())
        {
          pCuCtxChroma->qgStart    = true;
          pCuCtxChroma->isDQPCoded = false;
        }
        if (cs.slice->getUseChromaQpAdj() && pPartitionerChroma->currQgChromaEnable())
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      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 )
      {
    
          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
          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;
      }
    
      // Predict QP on start of quantization group
    
      if( cuCtx.qgStart )
      {
        cuCtx.qgStart = false;
    
        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
    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 );
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      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 );
    }
    
    
    //================================================================================
    //  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
    
    Yu Han's avatar
    Yu Han committed
      if ((!cs.slice->isIntra() || cs.slice->getSPS()->getIBCFlag()) && 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 );
    
    
    #if FIX_PCM
      // pcm samples
      if( CU::isIntra(cu) )
      {
        pcm_data( cu, partitioner );
        if( cu.ipcm )
        {
          end_of_ctu( cu, cuCtx );
          return;
        }
      }
    #endif
    
      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 );
    
    Yu Han's avatar
    Yu Han committed
      if (cu.slice->isIntra() && cu.cs->slice->getSPS()->getIBCFlag())
    
    Yu Han's avatar
    Yu Han committed
        m_BinEncoder.encodeBin((cu.skip), Ctx::SkipFlag(ctxId));
    
    Yu Han's avatar
    Yu Han committed
        DTRACE(g_trace_ctx, D_SYNTAX, "cu_skip_flag() ctx=%d skip=%d\n", ctxId, cu.skip ? 1 : 0);
        return;
      }
    
    
      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 );
    
    Yu Han's avatar
    Yu Han committed
      if (cu.skip && cu.cs->slice->getSPS()->getIBCFlag())
    
    Yu Han's avatar
    Yu Han committed
        unsigned ctxidx = DeriveCtx::CtxIBCFlag(cu);
        m_BinEncoder.encodeBin(CU::isIBC(cu) ? 1 : 0, Ctx::IBCFlag(ctxidx));
        DTRACE(g_trace_ctx, D_SYNTAX, "ibc() ctx=%d cu.predMode=%d\n", ctxidx, cu.predMode);
    
    Frank Bossen's avatar
    Frank Bossen committed
    #if !JVET_MMVD_OFF_MACRO
    
    Yu Han's avatar
    Yu Han committed
        if (CU::isInter(cu))
        {
          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);
        }
    
    Frank Bossen's avatar
    Frank Bossen committed
    #endif
    
    Frank Bossen's avatar
    Frank Bossen committed
    #if !JVET_MMVD_OFF_MACRO
    
    Yu Han's avatar
    Yu Han committed
      if (cu.skip && !cu.cs->slice->getSPS()->getIBCFlag())
    
    Yu Han's avatar
    Yu Han committed
      {
        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);
      }
    
    Frank Bossen's avatar
    Frank Bossen committed
    #endif
    
    Yu Han's avatar
    Yu Han committed
      if (cu.cs->slice->getSPS()->getIBCFlag())
    
    Yu Han's avatar
    Yu Han committed
      {
        if (cu.cs->slice->isIntra())
        {
    
    Yu Han's avatar
    Yu Han committed
          unsigned ctxidx = DeriveCtx::CtxIBCFlag(cu);
          m_BinEncoder.encodeBin(CU::isIBC(cu), Ctx::IBCFlag(ctxidx));
    
    Yu Han's avatar
    Yu Han committed
        }
        else
        {
          m_BinEncoder.encodeBin((CU::isIntra(cu)), Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu)));
          if (!CU::isIntra(cu))
          {
    
    Yu Han's avatar
    Yu Han committed
            unsigned ctxidx = DeriveCtx::CtxIBCFlag(cu);
            m_BinEncoder.encodeBin(CU::isIBC(cu), Ctx::IBCFlag(ctxidx));
    
    Yu Han's avatar
    Yu Han committed
          }
        }
      }
      else
      {
        if (cu.cs->slice->isIntra())
        {
          return;
        }
        m_BinEncoder.encodeBin((CU::isIntra(cu)), Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu)));
    
    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 );
    
      affine_amvr_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)];
    
    
      const int32_t numGBi = (cu.slice->getCheckLDC()) ? 5 : 3;
    
      m_BinEncoder.encodeBin((gbiCodingIdx == 0 ? 1 : 0), Ctx::GBiIdx(0));
    
    
      if(numGBi > 2 && gbiCodingIdx != 0)
      {
    
        const uint32_t prefixNumBits = numGBi - 2;
        const uint32_t step = 1;
    
    #if !JVET_N0286_SIMPLIFIED_GBI_IDX
    
        int ctxIdGBi = 4;
    
        uint8_t idx = 1;
        for(int ui = 0; ui < prefixNumBits; ++ui)
        {
    
          if (gbiCodingIdx == idx)
    
    #if JVET_N0286_SIMPLIFIED_GBI_IDX
            m_BinEncoder.encodeBinEP(1);
    #else
    
            m_BinEncoder.encodeBin(1, Ctx::GBiIdx(ctxIdGBi));
    
    #if JVET_N0286_SIMPLIFIED_GBI_IDX
            m_BinEncoder.encodeBinEP(0);
    #else
    
            m_BinEncoder.encodeBin(0, Ctx::GBiIdx(ctxIdGBi));
            ctxIdGBi += 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)
    {
    
    Liang Zhao's avatar
    Liang Zhao committed
    #if !ENABLE_JVET_L0283_MRL
      return;
    #endif
    
    
      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)
    {
    
    Liang Zhao's avatar
    Liang Zhao committed
    #if !ENABLE_JVET_L0283_MRL
      return;
    #endif
    
    
      if (!cu.Y().valid() || cu.predMode != MODE_INTRA || !isLuma(cu.chType) || cu.ipcm)
    
      {
        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];
    
        unsigned&  mpm_idx    = mpm_idxs[k];
        unsigned&  ipred_mode = ipred_modes[k];
    
        PU::getIntraMPMs( *pu, mpm_pred );
    
        ipred_mode = pu->intraDir[0];
        mpm_idx    = numMPMs;
        for( unsigned idx = 0; idx < numMPMs; idx++ )
        {
          if( ipred_mode == mpm_pred[idx] )
          {
            mpm_idx = idx;
            break;
          }
        }
    
        if( pu->multiRefIdx || ( cu.ispMode && isLuma( cu.chType ) ) )
    
        {
          CHECK(mpm_idx >= numMPMs, "use of non-MPM");
        }
        else
    
    Frank Bossen's avatar
    Frank Bossen committed
        {
          m_BinEncoder.encodeBin(mpm_idx < numMPMs, Ctx::IntraLumaMpmFlag());
        }
    
    
        pu = pu->next;
      }
    
      pu = cu.firstPU;
    
      // mpm_idx / rem_intra_luma_pred_mode
      for( int k = 0; k < numBlocks; k++ )
      {
        const unsigned& mpm_idx = mpm_idxs[k];
        if( mpm_idx < numMPMs )
        {
          {
    
    #if JVET_N0185_UNIFIED_MPM
            unsigned ctx = (pu->cu->ispMode == NOT_INTRA_SUBPARTITIONS ? 1 : 0);
            if (pu->multiRefIdx == 0)
    
              m_BinEncoder.encodeBin(mpm_idx > 0, Ctx::IntraLumaPlanarFlag(ctx));
    
            if( mpm_idx )
            {
              m_BinEncoder.encodeBinEP( mpm_idx > 1 );
            }
    
    ling's avatar
    ling committed
            if (mpm_idx > 1)
            {
              m_BinEncoder.encodeBinEP(mpm_idx > 2);
            }
            if (mpm_idx > 2)
            {
              m_BinEncoder.encodeBinEP(mpm_idx > 3);
            }
            if (mpm_idx > 3)
            {
              m_BinEncoder.encodeBinEP(mpm_idx > 4);
            }
    
          }
        }
        else
        {
          unsigned* mpm_pred   = mpm_preds[k];
          unsigned  ipred_mode = ipred_modes[k];
    
          // sorting of MPMs
          std::sort( mpm_pred, mpm_pred + numMPMs );
    
          {
    
            for (int idx = numMPMs - 1; idx >= 0; idx--)
    
            {
              if (ipred_mode > mpm_pred[idx])
              {
                ipred_mode--;
              }
            }
            CHECK(ipred_mode >= 64, "Incorrect mode");
    
    ling's avatar
    ling committed
            xWriteTruncBinCode(ipred_mode, NUM_LUMA_MODE - NUM_MOST_PROBABLE_MODES);  // Remaining mode is truncated binary coded