Skip to content
Snippets Groups Projects
CABACWriter.cpp 114 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
      //===== encode sign's =====
      unsigned numSigns = numNonZero;
      if( cctx.hideSign( firstNZPos, lastNZPos ) )
      {
        numSigns    --;
        signPattern >>= 1;
      }
      m_BinEncoder.encodeBinsEP( signPattern, numSigns );
    }
    
    
    void CABACWriter::residual_codingTS( const TransformUnit& tu, ComponentID compID )
    {
      DTRACE( g_trace_ctx, D_SYNTAX, "residual_codingTS() etype=%d pos=(%d,%d) size=%dx%d\n", tu.blocks[compID].compID, tu.blocks[compID].x, tu.blocks[compID].y, tu.blocks[compID].width, tu.blocks[compID].height );
    
      // init coeff coding context
    
      CoeffCodingContext  cctx    ( tu, compID, false, tu.cu->bdpcmMode );
    
      const TCoeff*       coeff   = tu.getCoeffs( compID ).buf;
    
      cctx.setNumCtxBins( 2 * tu.lwidth()*tu.lheight() );
    
      // determine and set last coeff position and sig group flags
      std::bitset<MLS_GRP_NUM> sigGroupFlags;
      for( int scanPos = 0; scanPos < cctx.maxNumCoeff(); scanPos++)
      {
        unsigned blkPos = cctx.blockPos( scanPos );
        if( coeff[blkPos] )
        {
          sigGroupFlags.set( scanPos >> cctx.log2CGSize() );
        }
      }
    
      // code subblocks
      for( int subSetId = 0; subSetId <= ( cctx.maxNumCoeff() - 1 ) >> cctx.log2CGSize(); subSetId++ )
      {
        cctx.initSubblock         ( subSetId, sigGroupFlags[subSetId] );
        residual_coding_subblockTS( cctx, coeff );
      }
    }
    
    void CABACWriter::residual_coding_subblockTS( CoeffCodingContext& cctx, const TCoeff* coeff )
    {
      //===== init =====
      const int   minSubPos   = cctx.maxSubPos();
      int         firstSigPos = cctx.minSubPos();
      int         nextSigPos  = firstSigPos;
    
      //===== encode significant_coeffgroup_flag =====
      if( !cctx.isLastSubSet() || !cctx.only1stSigGroup() )
      {
    
        if( cctx.isSigGroup() )
    
    #if !JVET_O0409_EXCLUDE_CODED_SUB_BLK_FLAG_FROM_COUNT
    
          if( cctx.isContextCoded() )
    
            m_BinEncoder.encodeBin( 1, cctx.sigGroupCtxId( true ) );
            DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() bin=%d ctx=%d\n", 1, cctx.sigGroupCtxId() );
    
    #if !JVET_O0409_EXCLUDE_CODED_SUB_BLK_FLAG_FROM_COUNT
    
            m_BinEncoder.encodeBinEP( 1 );
            DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() EPbin=%d\n", 1 );
    
    #if !JVET_O0409_EXCLUDE_CODED_SUB_BLK_FLAG_FROM_COUNT
    
          if( cctx.isContextCoded() )
    
            m_BinEncoder.encodeBin( 0, cctx.sigGroupCtxId( true ) );
            DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() bin=%d ctx=%d\n", 0, cctx.sigGroupCtxId() );
    
    #if !JVET_O0409_EXCLUDE_CODED_SUB_BLK_FLAG_FROM_COUNT
    
            m_BinEncoder.encodeBinEP( 0 );
            DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() EPbin=%d\n", 0 );
    
          return;
        }
      }
    
      //===== encode absolute values =====
      const int inferSigPos   = minSubPos;
      int       remAbsLevel   = -1;
      int       numNonZero    =  0;
    
    
    #if JVET_O0122_TS_SIGN_LEVEL
      int rightPixel, belowPixel, modAbsCoeff;
    #endif
    
    
      for( ; nextSigPos <= minSubPos; nextSigPos++ )
      {
        TCoeff    Coeff      = coeff[ cctx.blockPos( nextSigPos ) ];
        unsigned  sigFlag    = ( Coeff != 0 );
        if( numNonZero || nextSigPos != inferSigPos )
        {
          if( cctx.isContextCoded() )
          {
            const unsigned sigCtxId = cctx.sigCtxIdAbsTS( nextSigPos, coeff );
            m_BinEncoder.encodeBin( sigFlag, sigCtxId );
            DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sig_bin() bin=%d ctx=%d\n", sigFlag, sigCtxId );
          }
          else
          {
            m_BinEncoder.encodeBinEP( sigFlag );
            DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sig_bin() EPbin=%d\n", sigFlag );
          }
        }
    
        if( sigFlag )
        {
          //===== encode sign's =====
          int sign = Coeff < 0;
          if( cctx.isContextCoded() )
          {
    
    #if JVET_O0122_TS_SIGN_LEVEL
            const unsigned signCtxId = cctx.signCtxIdAbsTS(nextSigPos, coeff, cctx.bdpcm());
            m_BinEncoder.encodeBin(sign, signCtxId);
    #else
    
            m_BinEncoder.encodeBin( sign, Ctx::TsResidualSign( cctx.bdpcm() ? 1 : 0 ) );
    
          }
          else
          {
            m_BinEncoder.encodeBinEP( sign );
          }
          numNonZero++;
    
    #if JVET_O0122_TS_SIGN_LEVEL
          cctx.neighTS(rightPixel, belowPixel, nextSigPos, coeff);
          modAbsCoeff = cctx.deriveModCoeff(rightPixel, belowPixel, abs(Coeff), cctx.bdpcm());
          remAbsLevel = modAbsCoeff - 1;
    #else
    
          remAbsLevel = abs( Coeff ) - 1;
    
    
          unsigned gt1 = !!remAbsLevel;
    
    #if JVET_O0122_TS_SIGN_LEVEL
          const unsigned gt1CtxId = cctx.lrg1CtxIdAbsTS(nextSigPos, coeff, cctx.bdpcm());
          if (cctx.isContextCoded())
          {
            m_BinEncoder.encodeBin(gt1, gt1CtxId);
            DTRACE(g_trace_ctx, D_SYNTAX_RESI, "ts_gt1_flag() bin=%d ctx=%d\n", gt1, gt1CtxId);
          }
          else
          {
            m_BinEncoder.encodeBinEP(gt1);
            DTRACE(g_trace_ctx, D_SYNTAX_RESI, "ts_gt1_flag() EPbin=%d\n", gt1);
          }
    #else
    
          if( cctx.isContextCoded() )
          {
            m_BinEncoder.encodeBin( gt1, cctx.greaterXCtxIdAbsTS(0) );
            DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt1_flag() bin=%d ctx=%d\n", gt1, cctx.greaterXCtxIdAbsTS(0) );
          }
          else
          {
            m_BinEncoder.encodeBinEP( gt1 );
            DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt1_flag() EPbin=%d\n", gt1 );
          }
    
    
          if( gt1 )
          {
            remAbsLevel  -= 1;
            if( cctx.isContextCoded() )
            {
              m_BinEncoder.encodeBin( remAbsLevel&1, cctx.parityCtxIdAbsTS() );
              DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_par_flag() bin=%d ctx=%d\n", remAbsLevel&1, cctx.parityCtxIdAbsTS() );
            }
            else
            {
              m_BinEncoder.encodeBinEP( remAbsLevel&1 );
              DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_par_flag() EPbin=%d\n", remAbsLevel&1 );
            }
          }
        }
      }
    
      int cutoffVal = 2;
      int numGtBins = 4;
    
    
    #if JVET_O0619_GTX_SINGLE_PASS_TS_RESIDUAL_CODING
      for (int scanPos = firstSigPos; scanPos <= minSubPos; scanPos++)
      {
    
    #if JVET_O0122_TS_SIGN_LEVEL
        unsigned absLevel;
        cctx.neighTS(rightPixel, belowPixel, scanPos, coeff);
        absLevel = cctx.deriveModCoeff(rightPixel, belowPixel, abs(coeff[cctx.blockPos(scanPos)]), cctx.bdpcm()); 
    #else
    
        unsigned absLevel = abs(coeff[cctx.blockPos(scanPos)]);
    
        cutoffVal = 2;
        for (int i = 0; i < numGtBins; i++)
        {
          if (absLevel >= cutoffVal)
          {
            unsigned gt2 = (absLevel >= (cutoffVal + 2));
            if (cctx.isContextCoded())
            {
              m_BinEncoder.encodeBin(gt2, cctx.greaterXCtxIdAbsTS(cutoffVal >> 1));
              DTRACE(g_trace_ctx, D_SYNTAX_RESI, "ts_gt%d_flag() bin=%d ctx=%d sp=%d coeff=%d\n", i, gt2, cctx.greaterXCtxIdAbsTS(cutoffVal >> 1), scanPos, min<int>(absLevel, cutoffVal + 2));
            }
            else
            {
              m_BinEncoder.encodeBinEP(gt2);
              DTRACE(g_trace_ctx, D_SYNTAX_RESI, "ts_gt%d_flag() EPbin=%d sp=%d coeff=%d\n", i, gt2, scanPos, min<int>(absLevel, cutoffVal + 2));
            }
          }
          cutoffVal += 2;
        }
      }
    #else
    
      for( int i = 0; i < numGtBins; i++ )
      {
        for( int scanPos = firstSigPos; scanPos <= minSubPos; scanPos++ )
        {
    
    #if JVET_O0122_TS_SIGN_LEVEL
          unsigned absLevel;
          cctx.neighTS(rightPixel, belowPixel, scanPos, coeff);
          absLevel = cctx.deriveModCoeff(rightPixel, belowPixel, abs(coeff[cctx.blockPos(scanPos)]), cctx.bdpcm()); 
    #else
    
          unsigned absLevel = abs( coeff[cctx.blockPos( scanPos )] );
    
          if( absLevel >= cutoffVal )
          {
            unsigned gt2 = ( absLevel >= ( cutoffVal + 2 ) );
            if( cctx.isContextCoded() )
            {
              m_BinEncoder.encodeBin( gt2, cctx.greaterXCtxIdAbsTS( cutoffVal>>1 ) );
              DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt%d_flag() bin=%d ctx=%d sp=%d coeff=%d\n", i, gt2, cctx.greaterXCtxIdAbsTS( cutoffVal>>1 ), scanPos, min<int>( absLevel, cutoffVal+2 ) );
            }
            else
            {
              m_BinEncoder.encodeBinEP( gt2 );
              DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt%d_flag() EPbin=%d sp=%d coeff=%d\n", i, gt2, scanPos, min<int>( absLevel, cutoffVal+2 ) );
            }
          }
        }
        cutoffVal += 2;
      }
    
    
      //===== coeff bypass ====
      for( int scanPos = firstSigPos; scanPos <= minSubPos; scanPos++ )
      {
    
    #if JVET_O0122_TS_SIGN_LEVEL
        unsigned absLevel;
        cctx.neighTS(rightPixel, belowPixel, scanPos, coeff);
        absLevel = cctx.deriveModCoeff(rightPixel, belowPixel, abs(coeff[cctx.blockPos(scanPos)]), cctx.bdpcm());
    #else
    
        TCoeff    Coeff     = coeff[ cctx.blockPos( scanPos ) ];
        unsigned  absLevel  = abs( Coeff );
    
        if( absLevel >= cutoffVal )
        {
          int       rice = cctx.templateAbsSumTS( scanPos, coeff );
          unsigned  rem  = ( absLevel - cutoffVal ) >> 1;
          m_BinEncoder.encodeRemAbsEP( rem, rice, cctx.extPrec(), cctx.maxLog2TrDRange() );
          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_rem_val() bin=%d ctx=%d sp=%d\n", rem, rice, scanPos );
        }
      }
    }
    
    
    
    
    
    
    //================================================================================
    //  clause 7.3.8.12
    //--------------------------------------------------------------------------------
    //    void  cross_comp_pred( tu, compID )
    //================================================================================
    
    void CABACWriter::cross_comp_pred( const TransformUnit& tu, ComponentID compID )
    {
      CHECK(!( !isLuma( compID ) ), "Unspecified error");
      signed char alpha   = tu.compAlpha[compID];
      unsigned    ctxBase = ( compID == COMPONENT_Cr ? 5 : 0 );
      if( alpha == 0 )
      {
        m_BinEncoder.encodeBin( 0, Ctx::CrossCompPred( ctxBase ) );
        DTRACE( g_trace_ctx, D_SYNTAX, "cross_comp_pred() etype=%d pos=(%d,%d) alpha=%d\n", compID, tu.blocks[compID].x, tu.blocks[compID].y, tu.compAlpha[compID] );
        return;
      }
    
      static const unsigned log2AbsAlphaMinus1Table[8] = { 0, 1, 1, 2, 2, 2, 3, 3 };
      unsigned sign = ( alpha < 0 );
      if( sign )
      {
        alpha = -alpha;
      }
      CHECK(!( alpha <= 8 ), "Unspecified error");
      m_BinEncoder.encodeBin( 1, Ctx::CrossCompPred(ctxBase) );
      if( alpha > 1)
      {
         m_BinEncoder.encodeBin( 1, Ctx::CrossCompPred(ctxBase+1) );
         unary_max_symbol( log2AbsAlphaMinus1Table[alpha-1]-1, Ctx::CrossCompPred(ctxBase+2), Ctx::CrossCompPred(ctxBase+3), 2 );
      }
      else
      {
         m_BinEncoder.encodeBin( 0, Ctx::CrossCompPred(ctxBase+1) );
      }
      m_BinEncoder.encodeBin( sign, Ctx::CrossCompPred(ctxBase+4) );
    
      DTRACE( g_trace_ctx, D_SYNTAX, "cross_comp_pred() etype=%d pos=(%d,%d) alpha=%d\n", compID, tu.blocks[compID].x, tu.blocks[compID].y, tu.compAlpha[compID] );
    }
    
    
    
    
    //================================================================================
    //  helper functions
    //--------------------------------------------------------------------------------
    //    void  unary_max_symbol  ( symbol, ctxId0, ctxIdN, maxSymbol )
    //    void  unary_max_eqprob  ( symbol,                 maxSymbol )
    //    void  exp_golomb_eqprob ( symbol, count )
    //================================================================================
    
    void CABACWriter::unary_max_symbol( unsigned symbol, unsigned ctxId0, unsigned ctxIdN, unsigned maxSymbol )
    {
      CHECK( symbol > maxSymbol, "symbol > maxSymbol" );
      const unsigned totalBinsToWrite = std::min( symbol + 1, maxSymbol );
      for( unsigned binsWritten = 0; binsWritten < totalBinsToWrite; ++binsWritten )
      {
        const unsigned nextBin = symbol > binsWritten;
        m_BinEncoder.encodeBin( nextBin, binsWritten == 0 ? ctxId0 : ctxIdN );
      }
    }
    
    
    void CABACWriter::unary_max_eqprob( unsigned symbol, unsigned maxSymbol )
    {
      if( maxSymbol == 0 )
      {
        return;
      }
      bool     codeLast = ( maxSymbol > symbol );
      unsigned bins     = 0;
      unsigned numBins  = 0;
      while( symbol-- )
      {
        bins   <<= 1;
        bins   ++;
        numBins++;
      }
      if( codeLast )
      {
        bins  <<= 1;
        numBins++;
      }
      CHECK(!( numBins <= 32 ), "Unspecified error");
      m_BinEncoder.encodeBinsEP( bins, numBins );
    }
    
    
    void CABACWriter::exp_golomb_eqprob( unsigned symbol, unsigned count )
    {
      unsigned bins    = 0;
      unsigned numBins = 0;
      while( symbol >= (unsigned)(1<<count) )
      {
        bins <<= 1;
        bins++;
        numBins++;
        symbol -= 1 << count;
        count++;
      }
      bins <<= 1;
      numBins++;
      bins = (bins << count) | symbol;
      numBins += count;
      CHECK(!( numBins <= 32 ), "Unspecified error");
      m_BinEncoder.encodeBinsEP( bins, numBins );
    }
    
    
    void CABACWriter::codeAlfCtuEnableFlags( CodingStructure& cs, ChannelType channel, AlfParam* alfParam)
    
    {
      if( isLuma( channel ) )
      {
        if (alfParam->enabledFlag[COMPONENT_Y])
          codeAlfCtuEnableFlags( cs, COMPONENT_Y, alfParam );
      }
      else
      {
        if (alfParam->enabledFlag[COMPONENT_Cb])
          codeAlfCtuEnableFlags( cs, COMPONENT_Cb, alfParam );
        if (alfParam->enabledFlag[COMPONENT_Cr])
          codeAlfCtuEnableFlags( cs, COMPONENT_Cr, alfParam );
      }
    }
    
    void CABACWriter::codeAlfCtuEnableFlags( CodingStructure& cs, ComponentID compID, AlfParam* alfParam)
    
    {
      uint32_t numCTUs = cs.pcv->sizeInCtus;
    
      for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ )
      {
        codeAlfCtuEnableFlag( cs, ctuIdx, compID, alfParam );
      }
    }
    
    
    void CABACWriter::codeAlfCtuEnableFlag( CodingStructure& cs, uint32_t ctuRsAddr, const int compIdx, AlfParam* alfParam)
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      const bool alfComponentEnabled = (alfParam != NULL) ? alfParam->enabledFlag[compIdx] : cs.slice->getTileGroupAlfEnabledFlag((ComponentID)compIdx);
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      if( cs.sps->getALFEnabledFlag() && alfComponentEnabled )
    
      {
        const PreCalcValues& pcv = *cs.pcv;
        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 uint32_t          curSliceIdx = cs.slice->getIndependentSliceIdx();
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        const uint32_t      curTileIdx = cs.picture->brickMap->getBrickIdxRsMap( pos );
    
        bool                leftAvail = cs.getCURestricted( pos.offset( -(int)pcv.maxCUWidth, 0 ), pos, curSliceIdx, curTileIdx, CH_L ) ? true : false;
        bool                aboveAvail = cs.getCURestricted( pos.offset( 0, -(int)pcv.maxCUHeight ), pos, curSliceIdx, curTileIdx, CH_L ) ? true : false;
    
    
        int leftCTUAddr = leftAvail ? ctuRsAddr - 1 : -1;
        int aboveCTUAddr = aboveAvail ? ctuRsAddr - frame_width_in_ctus : -1;
    
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        uint8_t* ctbAlfFlag = cs.slice->getPic()->getAlfCtuEnableFlag( compIdx );
        int ctx = 0;
        ctx += leftCTUAddr > -1 ? ( ctbAlfFlag[leftCTUAddr] ? 1 : 0 ) : 0;
        ctx += aboveCTUAddr > -1 ? ( ctbAlfFlag[aboveCTUAddr] ? 1 : 0 ) : 0;
        m_BinEncoder.encodeBin( ctbAlfFlag[ctuRsAddr], Ctx::ctbAlfFlag( compIdx * 3 + ctx ) );
    
    void CABACWriter::code_unary_fixed( unsigned symbol, unsigned ctxId, unsigned unary_max, unsigned fixed )
    {
      bool unary = (symbol <= unary_max);
      m_BinEncoder.encodeBin( unary, ctxId );
      if( unary )
      {
        unary_max_eqprob( symbol, unary_max );
      }
      else
      {
        m_BinEncoder.encodeBinsEP( symbol - unary_max - 1, fixed );
      }
    }
    
    void CABACWriter::mip_flag( const CodingUnit& cu )
    {
      if( !cu.Y().valid() )
      {
        return;
      }
      if( !cu.cs->sps->getUseMIP() )
      {
        return;
      }
      if( cu.lwidth() > MIP_MAX_WIDTH || cu.lheight() > MIP_MAX_HEIGHT )
      {
        return;
      }
      if( !mipModesAvailable( cu.Y() ) )
      {
        return;
      }
    
      unsigned ctxId = DeriveCtx::CtxMipFlag( cu );
      m_BinEncoder.encodeBin( cu.mipFlag, Ctx::MipFlag( ctxId ) );
      DTRACE( g_trace_ctx, D_SYNTAX, "mip_flag() pos=(%d,%d) mode=%d\n", cu.lumaPos().x, cu.lumaPos().y, cu.mipFlag ? 1 : 0 );
    }
    
    void CABACWriter::mip_pred_modes( const CodingUnit& cu )
    {
      if( !cu.Y().valid() )
      {
        return;
      }
      for( const auto &pu : CU::traversePUs( cu ) )
      {
        mip_pred_mode( pu );
      }
    }
    
    void CABACWriter::mip_pred_mode( const PredictionUnit& pu )
    {
      const int numModes = getNumModesMip( pu.Y() ); CHECKD( numModes > MAX_NUM_MIP_MODE, "Error: too many MIP modes" );
    
      // derive modeIdx from true MIP mode
      unsigned mpm[NUM_MPM_MIP];
      PU::getMipMPMs(pu, mpm);
    
      unsigned mipMode = pu.intraDir[CHANNEL_TYPE_LUMA];
      unsigned mpmIdx   = NUM_MPM_MIP;
      for( auto k = 0; k < NUM_MPM_MIP; k++ )
      {
        if( mipMode == mpm[k] )
        {
          mpmIdx = k;
          break;
        }
      }
    
      unsigned modeIdx;
      if (mpmIdx < NUM_MPM_MIP)
      {
        modeIdx = mpmIdx;
      }
      else
      {
        std::sort( mpm, mpm + NUM_MPM_MIP);
    
        modeIdx = mipMode;
        for( auto k = (NUM_MPM_MIP - 1); k >= 0; k-- )
        {
          if( modeIdx > mpm[k] )
          {
            modeIdx--;
          }
        }
        CHECK( modeIdx >= (1<<getNumEpBinsMip( pu.Y() )), "Incorrect mode" );
        modeIdx += NUM_MPM_MIP;
      }
    
      CHECK( modeIdx >= numModes, "modeIdx out of range" );
      int unaryMax = NUM_MPM_MIP - 1;
      int fixedLength = getNumEpBinsMip( pu.Y() );
      code_unary_fixed( modeIdx, Ctx::MipMode( 0 ), unaryMax, fixedLength );
    
      DTRACE( g_trace_ctx, D_SYNTAX, "mip_pred_mode() pos=(%d,%d) mode=%d\n", pu.lumaPos().x, pu.lumaPos().y, pu.intraDir[CHANNEL_TYPE_LUMA] );
    }
    
    Karsten Suehring's avatar
    Karsten Suehring committed
    void CABACWriter::codeAlfCtuFilterIndex(CodingStructure& cs, uint32_t ctuRsAddr, bool alfEnableLuma)
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      if ( (!cs.sps->getALFEnabledFlag()) || (!alfEnableLuma))
    
      {
        return;
      }
    
      uint8_t* ctbAlfFlag = cs.slice->getPic()->getAlfCtuEnableFlag(COMPONENT_Y);
      if (!ctbAlfFlag[ctuRsAddr])
      {
        return;
      }
    
      short* alfCtbFilterIndex = cs.slice->getPic()->getAlfCtbFilterIndex();
      const unsigned filterSetIdx = alfCtbFilterIndex[ctuRsAddr];
      unsigned numAps = cs.slice->getTileGroupNumAps();
      unsigned numAvailableFiltSets = numAps + NUM_FIXED_FILTER_SETS;
      if (numAvailableFiltSets > NUM_FIXED_FILTER_SETS)
      {
        int useLatestFilt = (filterSetIdx == NUM_FIXED_FILTER_SETS) ? 1 : 0;
        m_BinEncoder.encodeBin(useLatestFilt, Ctx::AlfUseLatestFilt());
        if (!useLatestFilt)
        {
    
          if (numAps == 1)
          {
            CHECK(filterSetIdx >= NUM_FIXED_FILTER_SETS, "fixed set numavail < num_fixed");
            xWriteTruncBinCode(filterSetIdx, NUM_FIXED_FILTER_SETS);
          }
          else
          {
            int useTemporalFilt = (filterSetIdx > NUM_FIXED_FILTER_SETS) ? 1 : 0;
            m_BinEncoder.encodeBin(useTemporalFilt, Ctx::AlfUseTemporalFilt());
    
            if (useTemporalFilt)
            {
              CHECK((filterSetIdx - (NUM_FIXED_FILTER_SETS + 1)) >= (numAvailableFiltSets - (NUM_FIXED_FILTER_SETS + 1)), "temporal non-latest set");
    
    #if JVET_O0247_ALF_CTB_CODING_REDUNDANCY_REMOVAL
              if (numAps > 2)
              {
    #endif
                xWriteTruncBinCode(filterSetIdx - (NUM_FIXED_FILTER_SETS + 1), numAvailableFiltSets - (NUM_FIXED_FILTER_SETS + 1));
    #if JVET_O0247_ALF_CTB_CODING_REDUNDANCY_REMOVAL
              }
    #endif
    
            }
            else
            {
              CHECK(filterSetIdx >= NUM_FIXED_FILTER_SETS, "fixed set larger than temporal");
              xWriteTruncBinCode(filterSetIdx, NUM_FIXED_FILTER_SETS);
            }
          }
        }
      }
      else
      {
        CHECK(filterSetIdx >= NUM_FIXED_FILTER_SETS, "fixed set numavail < num_fixed");
        xWriteTruncBinCode(filterSetIdx, NUM_FIXED_FILTER_SETS);
      }
    }
    
    #if JVET_O0090_ALF_CHROMA_FILTER_ALTERNATIVES_CTB
    void CABACWriter::codeAlfCtuAlternatives( CodingStructure& cs, ChannelType channel, AlfParam* alfParam)
    {
      if( isChroma( channel ) )
      {
        if (alfParam->enabledFlag[COMPONENT_Cb])
          codeAlfCtuAlternatives( cs, COMPONENT_Cb, alfParam );
        if (alfParam->enabledFlag[COMPONENT_Cr])
          codeAlfCtuAlternatives( cs, COMPONENT_Cr, alfParam );
      }
    }
    void CABACWriter::codeAlfCtuAlternatives( CodingStructure& cs, ComponentID compID, AlfParam* alfParam)
    {
      if( compID == COMPONENT_Y )
        return;
      uint32_t numCTUs = cs.pcv->sizeInCtus;
      uint8_t* ctbAlfFlag = cs.slice->getPic()->getAlfCtuEnableFlag( compID );
    
      for( int ctuIdx = 0; ctuIdx < numCTUs; ctuIdx++ )
      {
        if( ctbAlfFlag[ctuIdx] )
        {
          codeAlfCtuAlternative( cs, ctuIdx, compID, alfParam );
        }
      }
    }
    
    void CABACWriter::codeAlfCtuAlternative( CodingStructure& cs, uint32_t ctuRsAddr, const int compIdx, const AlfParam* alfParam)
    {
      if( compIdx == COMPONENT_Y )
        return;
      int apsIdx = alfParam ? 0 : cs.slice->getTileGroupApsIdChroma();
      const AlfParam& alfParamRef = alfParam ? (*alfParam) : cs.slice->getAlfAPSs()[apsIdx]->getAlfAPSParam();
    
      if( alfParam || (cs.sps->getALFEnabledFlag() && cs.slice->getTileGroupAlfEnabledFlag( (ComponentID)compIdx )) )
      {
        uint8_t* ctbAlfFlag = cs.slice->getPic()->getAlfCtuEnableFlag( compIdx );
    
        if( ctbAlfFlag[ctuRsAddr] )
        {
          const int numAlts = alfParamRef.numAlternativesChroma;
          uint8_t* ctbAlfAlternative = cs.slice->getPic()->getAlfCtuAlternativeData( compIdx );
          unsigned numOnes = ctbAlfAlternative[ctuRsAddr];
          assert( ctbAlfAlternative[ctuRsAddr] < numAlts );
          for( int i = 0; i < numOnes; ++i )
            m_BinEncoder.encodeBin( 1, Ctx::ctbAlfAlternative( compIdx-1 ) );
          if( numOnes < numAlts-1 )
            m_BinEncoder.encodeBin( 0, Ctx::ctbAlfAlternative( compIdx-1 ) );
        }
      }
    }
    
    #endif