diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index ca1fcc142b6294d867d4c49af4c2a63275dcc7ca..b678584b3ec2c3c2f9196b080459aa1b8a0c8019 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -818,6 +818,11 @@ void EncApp::xInitLibCfg()
   m_cEncLib.setBIFStrength                                       ( m_BIFStrength );
   m_cEncLib.setBIFQPOffset                                       ( m_BIFQPOffset );
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  m_cEncLib.setUseCBIF                                            ( m_CBIF );
+  m_cEncLib.setCBIFStrength                                       ( m_CBIFStrength );
+  m_cEncLib.setCBIFQPOffset                                       ( m_CBIFQPOffset );
+#endif
 
   // ADD_NEW_TOOL : (encoder app) add setting of tool enabling flags and associated parameters here
   m_cEncLib.setVirtualBoundariesEnabledFlag                      ( m_virtualBoundariesEnabledFlag );
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 06a822fda82092c00e699e35b977a36f9ba95d86..8112500c0320be6e81df91bd1ed267acf66b83c9 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1064,6 +1064,11 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("BIF",                                             m_BIF,                                            true, "bilateral filter   (0: off, 1:on)  [default: on]")
   ("BIFStrength",                                     m_BIFStrength,                                       1u, "bilateral filter strength  (0: half, 1: full, 2: double)  [default: full]")
   ("BIFQPOffset",                                     m_BIFQPOffset,                                        0, "bilateral filter QP offset (0: no offset)  [default: 0]")
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  ("CBIF",                                             m_CBIF,                                            true, "chroma bilateral filter   (0: off, 1:on)  [default: on]")
+  ("CBIFStrength",                                     m_CBIFStrength,                                       1u, "chroma bilateral filter strength  (0: half, 1: full, 2: double)  [default: full]")
+  ("CBIFQPOffset",                                     m_CBIFQPOffset,                                        0, "chroma bilateral filter QP offset (0: no offset)  [default: 0]")
 #endif
   // ADD_NEW_TOOL : (encoder app) add parsing parameters here
   ( "VirtualBoundariesPresentInSPSFlag",              m_virtualBoundariesPresentFlag,                    true, "Virtual Boundary position information is signalled in SPS or PH (1:SPS, 0:PH)  [default: on]" )
@@ -4361,6 +4366,11 @@ void EncAppCfg::xPrintParameter()
   msg( VERBOSE, "BIF:%d ", m_BIF);
   msg( VERBOSE, "BIFStrength:%d ", m_BIFStrength);
   msg( VERBOSE, "BIFQPOffset:%d ", m_BIFQPOffset);
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  msg( VERBOSE, "CBIF:%d ", m_CBIF);
+  msg( VERBOSE, "CBIFStrength:%d ", m_CBIFStrength);
+  msg( VERBOSE, "CBIFQPOffset:%d ", m_CBIFQPOffset);
 #endif
   // ADD_NEW_TOOL (add some output indicating the usage of tools)
   msg( VERBOSE, "VirtualBoundariesEnabledFlag:%d ", m_virtualBoundariesEnabledFlag );
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 23b3207f30daafe08f032819e8a9fff6bf1f5842..45bd7a81cf9d066312e63877fb351902c30dc34c 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -432,6 +432,11 @@ protected:
   unsigned  m_BIFStrength;                                    /// Bilateral filter strength
   int       m_BIFQPOffset;                                    /// Bilateral QP offset
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  bool      m_CBIF;
+  unsigned  m_CBIFStrength;
+  int       m_CBIFQPOffset;
+#endif
   
   // ADD_NEW_TOOL : (encoder app) add tool enabling flags and associated parameters here
   bool      m_virtualBoundariesEnabledFlag;
diff --git a/source/Lib/CommonLib/AdaptiveLoopFilter.cpp b/source/Lib/CommonLib/AdaptiveLoopFilter.cpp
index 5a32244555c875377e13691fae18594827e55880..35e34d36743fd6851877b0616c5cf8de6bb8794f 100644
--- a/source/Lib/CommonLib/AdaptiveLoopFilter.cpp
+++ b/source/Lib/CommonLib/AdaptiveLoopFilter.cpp
@@ -47,8 +47,16 @@
 constexpr int AdaptiveLoopFilter::AlfNumClippingValues[];
 
 AdaptiveLoopFilter::AdaptiveLoopFilter()
+#if !JVET_X0071_ALF_BAND_CLASSIFIER
   : m_classifier( nullptr )
+#endif
 {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  for( int i = 0; i < ALF_NUM_CLASSIFIER; i++ )
+  {
+    m_classifier[i] = nullptr;
+  }
+#endif
   for (size_t i = 0; i < NUM_DIRECTIONS; i++)
   {
     m_laplacian[i] = m_laplacianPtr[i];
@@ -88,6 +96,9 @@ AdaptiveLoopFilter::AdaptiveLoopFilter()
   m_deriveClassificationLaplacianBig = deriveClassificationLaplacianBig;
   m_calcClass0 = calcClass;
   m_calcClass1 = calcClass;
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  m_calcClass2 = calcClassNew;
+#endif
   m_fixFilterResult = nullptr;
 #else
   m_deriveClassificationBlk = deriveClassificationBlk;
@@ -540,11 +551,15 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs)
               const Area blkSrc( 0, 0, w, h );
               const Area blkDst( xStart, yStart, w, h );
               short filterSetIndex = alfCtuFilterIndex[ctuIdx];
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+              deriveClassification( m_classifier, buf.get(COMPONENT_Y), blkDst, blkSrc, cs, filterSetIndex < NUM_FIXED_FILTER_SETS ? filterSetIndex : -1, filterSetIndex < NUM_FIXED_FILTER_SETS ? -1 : m_classifierIdxApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][m_ctuAlternative[COMPONENT_Y][ctuIdx]] );
+#else
               deriveClassification( m_classifier, buf.get( COMPONENT_Y ), blkDst, blkSrc 
 #if ALF_IMPROVEMENT
               , cs, filterSetIndex < NUM_FIXED_FILTER_SETS ? filterSetIndex : -1
 #endif
               );
+#endif
 
               short *coeff;
 #if JVET_R0351_HIGH_BIT_DEPTH_SUPPORT
@@ -563,7 +578,12 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs)
                 coeff = m_coeffApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
                 clip = m_clippApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
                 AlfFilterType filterTypeCtb = m_filterTypeApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS];
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+                int classifierIdx = m_classifierIdxApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
+                alfFiltering( m_classifier[classifierIdx], recYuv, buf, blkDst, blkSrc, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs, filterTypeCtb, m_fixFilterResult, fixedFilterSetIdx );
+#else
                 alfFiltering( m_classifier, recYuv, buf, blkDst, blkSrc, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs, filterTypeCtb, m_fixFilterResult, fixedFilterSetIdx );
+#endif
               }
 #else
               if( filterSetIndex >= NUM_FIXED_FILTER_SETS )
@@ -592,7 +612,11 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs)
                 const Area blkDst( xStart >> chromaScaleX, yStart >> chromaScaleY, w >> chromaScaleX, h >> chromaScaleY );
                 uint8_t alt_num = m_ctuAlternative[compIdx][ctuIdx];
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+                alfFiltering( m_classifier[0], recYuv, buf, blkDst, blkSrc, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_filterTypeApsChroma, nullptr, -1 );
+#else
                 alfFiltering( m_classifier, recYuv, buf, blkDst, blkSrc, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_filterTypeApsChroma, nullptr, -1 );
+#endif
 #else
                 m_filter5x5Blk(m_classifier, recYuv, buf, blkDst, blkSrc, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_alfVBChmaCTUHeight, m_alfVBChmaPos );
 #endif
@@ -629,11 +653,15 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs)
         {
           Area blk( xPos, yPos, width, height );
           short filterSetIndex = alfCtuFilterIndex[ctuIdx];
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          deriveClassification( m_classifier, tmpYuv.get(COMPONENT_Y), blk, blk, cs, filterSetIndex < NUM_FIXED_FILTER_SETS ? filterSetIndex : -1, filterSetIndex < NUM_FIXED_FILTER_SETS ? -1 : m_classifierIdxApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][m_ctuAlternative[COMPONENT_Y][ctuIdx]] );
+#else
           deriveClassification( m_classifier, tmpYuv.get( COMPONENT_Y ), blk, blk 
 #if ALF_IMPROVEMENT
             , cs, filterSetIndex < NUM_FIXED_FILTER_SETS ? filterSetIndex : -1
 #endif
-          );          
+          );      
+#endif
           short *coeff;
 #if JVET_R0351_HIGH_BIT_DEPTH_SUPPORT
           Pel *clip;
@@ -651,7 +679,12 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs)
             coeff = m_coeffApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
             clip = m_clippApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
             AlfFilterType filterTypeCtb = m_filterTypeApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS];
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+            int classifierIdx = m_classifierIdxApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
+            alfFiltering( m_classifier[classifierIdx], recYuv, tmpYuv, blk, blk, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs, filterTypeCtb, m_fixFilterResult, fixedFilterSetIdx );
+#else
             alfFiltering( m_classifier, recYuv, tmpYuv, blk, blk, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs, filterTypeCtb, m_fixFilterResult, fixedFilterSetIdx );
+#endif
           }
 #else
           if( filterSetIndex >= NUM_FIXED_FILTER_SETS )
@@ -680,7 +713,11 @@ void AdaptiveLoopFilter::ALFProcess(CodingStructure& cs)
             uint8_t alt_num = m_ctuAlternative[compIdx][ctuIdx];
 
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+            alfFiltering( m_classifier[0], recYuv, tmpYuv, blk, blk, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_filterTypeApsChroma  , nullptr, -1 );
+#else
             alfFiltering( m_classifier, recYuv, tmpYuv, blk, blk, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_filterTypeApsChroma  , nullptr, -1 );
+#endif
 #else
             m_filter5x5Blk( m_classifier, recYuv, tmpYuv, blk, blk, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_alfVBChmaCTUHeight, m_alfVBChmaPos );
 #endif
@@ -727,6 +764,9 @@ void AdaptiveLoopFilter::reconstructCoeffAPSs(CodingStructure& cs, bool luma, bo
       reconstructCoeff(alfParamTmp, CHANNEL_TYPE_LUMA, isRdo, true);
       memcpy(m_coeffApsLuma[i], m_coeffFinal, sizeof(m_coeffFinal));
       memcpy(m_clippApsLuma[i], m_clippFinal, sizeof(m_clippFinal));
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      memcpy(m_classifierIdxApsLuma[i], m_classifierFinal, sizeof(m_classifierFinal));
+#endif
 #if ALF_IMPROVEMENT
       m_filterTypeApsLuma[i] = alfParamTmp.filterType[CHANNEL_TYPE_LUMA];
       m_numLumaAltAps[i] = alfParamTmp.numAlternativesLuma;
@@ -808,6 +848,9 @@ void AdaptiveLoopFilter::reconstructCoeff( AlfParam& alfParam, ChannelType chann
       m_chromaClippFinal[altIdx][numCoeffMinus1] = isRdo ? 0 : m_alfClippingValues[channel][0];
       continue;
     }
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    m_classifierFinal[altIdx] = alfParam.lumaClassifierIdx[altIdx];
+#endif
     for( int classIdx = 0; classIdx < numClasses; classIdx++ )
     {
 #if ALF_IMPROVEMENT
@@ -965,6 +1008,21 @@ void AdaptiveLoopFilter::create(const int picWidth, const int picHeight, const C
 #endif
 
   // Classification
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  for( int classifier = 0; classifier < ALF_NUM_CLASSIFIER; classifier++ )
+  {
+    if( m_classifier[classifier] == nullptr )
+    {
+      m_classifier[classifier] = new AlfClassifier*[picHeight];
+      m_classifier[classifier][0] = new AlfClassifier[picWidth * picHeight];
+
+      for( int i = 1; i < picHeight; i++ )
+      {
+        m_classifier[classifier][i] = m_classifier[classifier][0] + i * picWidth;
+      }
+    }
+  }
+#else
   if ( m_classifier == nullptr )
   {
     m_classifier = new AlfClassifier*[picHeight];
@@ -975,6 +1033,7 @@ void AdaptiveLoopFilter::create(const int picWidth, const int picHeight, const C
       m_classifier[i] = m_classifier[0] + i * picWidth;
     }
   }
+#endif
 #if !ALF_IMPROVEMENT
   for (int filterSetIndex = 0; filterSetIndex < NUM_FIXED_FILTER_SETS; filterSetIndex++)
   {
@@ -1006,12 +1065,24 @@ void AdaptiveLoopFilter::destroy()
     return;
   }
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  for (int classifier = 0; classifier < ALF_NUM_CLASSIFIER; classifier++)
+  {
+    if (m_classifier[classifier])
+    {
+      delete[] m_classifier[classifier][0];
+      delete[] m_classifier[classifier];
+      m_classifier[classifier] = nullptr;
+    }
+  }
+#else
   if( m_classifier )
   {
     delete[] m_classifier[0];
     delete[] m_classifier;
     m_classifier = nullptr;
   }
+#endif
 
 #if ALF_IMPROVEMENT
   if( m_fixFilterResult )
@@ -1113,11 +1184,15 @@ void  AdaptiveLoopFilter::alfFiltering( AlfClassifier **classifier, const PelUni
 }
 #endif
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+void AdaptiveLoopFilter::deriveClassification( AlfClassifier*** classifier, const CPelBuf& srcLuma, const Area& blkDst, const Area& blk, CodingStructure &cs, const int classifierIdx, const int multipleClassifierIdx )
+#else
 void AdaptiveLoopFilter::deriveClassification( AlfClassifier** classifier, const CPelBuf& srcLuma, const Area& blkDst, const Area& blk 
 #if ALF_IMPROVEMENT
   , CodingStructure &cs, const int classifierIdx
 #endif
 )
+#endif
 {
 #if ALF_IMPROVEMENT
   if( cs.slice->getCuQpDeltaSubdiv() )
@@ -1125,7 +1200,11 @@ void AdaptiveLoopFilter::deriveClassification( AlfClassifier** classifier, const
     UnitArea curArea( cs.area.chromaFormat, blkDst );
     for( auto &currCU : cs.traverseCUs( curArea, CH_L ) )
     {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      deriveClassificationAndFixFilterResultsBlk( classifier, m_fixFilterResult, srcLuma, Area(currCU.lumaPos().x, currCU.lumaPos().y, currCU.lwidth(), currCU.lheight()), Area(currCU.lumaPos().x, currCU.lumaPos().y, currCU.lwidth(), currCU.lheight()), m_inputBitDepth[CHANNEL_TYPE_LUMA], cs, m_clpRngs.comp[COMPONENT_Y], m_alfClippingValues[CHANNEL_TYPE_LUMA], currCU.qp, cs.slice->getTileGroupAlfFixedFilterSetIdx(), m_mappingDir, m_laplacian, classifierIdx, multipleClassifierIdx );
+#else
       deriveClassificationAndFixFilterResultsBlk( classifier, m_fixFilterResult, srcLuma, Area(currCU.lumaPos().x, currCU.lumaPos().y, currCU.lwidth(), currCU.lheight()), Area(currCU.lumaPos().x, currCU.lumaPos().y, currCU.lwidth(), currCU.lheight()), m_inputBitDepth[CHANNEL_TYPE_LUMA], cs, m_clpRngs.comp[COMPONENT_Y], m_alfClippingValues[CHANNEL_TYPE_LUMA], currCU.qp, cs.slice->getTileGroupAlfFixedFilterSetIdx(), m_mappingDir, m_laplacian, classifierIdx );
+#endif
     }
   }
   else
@@ -1139,7 +1218,11 @@ void AdaptiveLoopFilter::deriveClassification( AlfClassifier** classifier, const
       for( int j = blk.pos().x; j < width; j += m_CLASSIFICATION_BLK_SIZE )
       {
         int nWidth = std::min( j + m_CLASSIFICATION_BLK_SIZE, width ) - j;
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        deriveClassificationAndFixFilterResultsBlk(classifier, m_fixFilterResult, srcLuma, Area(j - blk.pos().x + blkDst.pos().x, i - blk.pos().y + blkDst.pos().y, nWidth, nHeight), Area(j, i, nWidth, nHeight), m_inputBitDepth[CHANNEL_TYPE_LUMA], cs, m_clpRngs.comp[COMPONENT_Y], m_alfClippingValues[CHANNEL_TYPE_LUMA], cs.slice->getSliceQp(), cs.slice->getTileGroupAlfFixedFilterSetIdx(), m_mappingDir, m_laplacian, classifierIdx, multipleClassifierIdx );
+#else
         deriveClassificationAndFixFilterResultsBlk( classifier, m_fixFilterResult, srcLuma, Area(j - blk.pos().x + blkDst.pos().x, i - blk.pos().y + blkDst.pos().y, nWidth, nHeight), Area(j, i, nWidth, nHeight), m_inputBitDepth[CHANNEL_TYPE_LUMA], cs, m_clpRngs.comp[COMPONENT_Y], m_alfClippingValues[CHANNEL_TYPE_LUMA], cs.slice->getSliceQp(), cs.slice->getTileGroupAlfFixedFilterSetIdx(), m_mappingDir, m_laplacian, classifierIdx );
+#endif
       }
     }
   }
@@ -1160,6 +1243,37 @@ void AdaptiveLoopFilter::deriveClassification( AlfClassifier** classifier, const
 #endif
 }
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+void AdaptiveLoopFilter::calcClassNew( AlfClassifier **classifier, const Area &blkDst, const Area &curBlk, const CPelBuf& srcLuma, int subBlkSize, AlfClassifier **classifier0, int classifierIdx, int bitDepth )
+{
+  const Pel* src = srcLuma.buf;
+  int stride = srcLuma.stride;
+  int yOffset = blkDst.pos().y * stride;
+  const Pel *src0 = &src[yOffset];
+  const Pel *src1 = &src[yOffset + stride];
+  int stride2 = 2 * stride;
+  for (int i = 0; i < blkDst.height; i += subBlkSize)
+  {
+    for (int j = 0; j < blkDst.width; j += subBlkSize)
+    {
+      int xOffset = blkDst.pos().x + j;
+      const Pel *pY0 = src0 + xOffset;
+      const Pel *pY1 = src1 + xOffset;
+      int sum = pY0[0] + pY0[1] + pY1[0] + pY1[1];
+      int classIdx = (sum * ALF_NUM_CLASSES_CLASSIFIER[classifierIdx]) >> (bitDepth + 2);
+      for (int ii = curBlk.y + i; ii < curBlk.y + i + subBlkSize; ii++)
+      {
+        for (int jj = curBlk.x + j; jj < curBlk.x + j + subBlkSize; jj++)
+        {
+          classifier[ii][jj] = classIdx << 2;
+        }
+      }
+    }
+    src0 += stride2;
+    src1 += stride2;
+  }
+}
+#endif
 #if ALF_IMPROVEMENT
 int AdaptiveLoopFilter::assignAct( int avg_varPrec, int shift, int noAct )
 {
@@ -1255,7 +1369,11 @@ void AdaptiveLoopFilter::calcClass(AlfClassifier **classifier, const Area &blkDs
       if (noAct == 5)
       {
         const int th[] = { 0, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4 };
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        activity = th[std::min((mult * 3 * sum) >> shift, 15)];
+#else
         activity = th[std::min((mult * sum) >> shift, 15)];
+#endif
       }
       else
       {
@@ -1592,7 +1710,11 @@ void AdaptiveLoopFilter::deriveClassificationLaplacian(const CPelBuf &srcLuma, c
   }
 }
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+void AdaptiveLoopFilter::deriveClassificationAndFixFilterResultsBlk( AlfClassifier ***classifier, Pel ***fixedFilterResults, const CPelBuf &srcLuma, const Area &blkDst, const Area &blk, const int bits, CodingStructure& cs, const ClpRng &clpRng, const Pel clippingValues[4], int qp, int fixedFilterSetIdx, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX], uint32_t **laplacian[NUM_DIRECTIONS], const int classifierIdx, const int multipleClassifierIdx )
+#else
 void AdaptiveLoopFilter::deriveClassificationAndFixFilterResultsBlk( AlfClassifier **classifier, Pel ***fixedFilterResults, const CPelBuf &srcLuma, const Area &blkDst, const Area &blk, const int bits, CodingStructure& cs, const ClpRng &clpRng, const Pel clippingValues[4], int qp, int fixedFilterSetIdx, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX], uint32_t **laplacian[NUM_DIRECTIONS], const int classifierIdx )
+#endif
 {
   m_deriveClassificationLaplacian(srcLuma, blkDst, blk, laplacian);
   
@@ -1628,12 +1750,20 @@ void AdaptiveLoopFilter::deriveClassificationAndFixFilterResultsBlk( AlfClassifi
             {
               m_deriveClassificationLaplacianBig(blkDst, laplacian);
             }
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+            m_calcClass0(classifier[0], blkDst, blkDst, usedWindowIdx[dirWindSize], 1, NUM_DIR_FIX, NUM_ACT_FIX, bits, 2, mappingDir, laplacian);
+#else
             m_calcClass0(classifier, blkDst, blkDst, usedWindowIdx[dirWindSize], 1, NUM_DIR_FIX, NUM_ACT_FIX, bits, 2, mappingDir, laplacian);
+#endif
 
             reuse = true;
           }
           //fixed filtering
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          m_filter13x13Blk(classifier[0], srcLuma, blkDst, fixedFilterResults, m_picWidth, fixedFiltInd, m_classIdnFixedFilter[fixedFiltSetInd][dirWindSize], fixedFiltSetInd, dirWindSize, clpRng, clippingValues);
+#else
           m_filter13x13Blk(classifier, srcLuma, blkDst, fixedFilterResults, m_picWidth, fixedFiltInd, m_classIdnFixedFilter[fixedFiltSetInd][dirWindSize], fixedFiltSetInd, dirWindSize, clpRng, clippingValues);
+#endif
         }
       }
       fixedFiltInd++;
@@ -1641,7 +1771,21 @@ void AdaptiveLoopFilter::deriveClassificationAndFixFilterResultsBlk( AlfClassifi
   }
   if( classifierIdx == -1 )
   {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    if( multipleClassifierIdx == ALF_NUM_CLASSIFIER || multipleClassifierIdx == 0 || multipleClassifierIdx == 1 )
+    {
+      m_calcClass1( classifier[0], blkDst, Area(blkDst.pos().x, blkDst.pos().y, blkDst.width, blkDst.height), 5, 0, 5, 5, bits, 2, mappingDir, laplacian );
+    }
+    for( int curClassifierIdx = 1; curClassifierIdx < ALF_NUM_CLASSIFIER; curClassifierIdx++ )
+    {
+      if( multipleClassifierIdx == ALF_NUM_CLASSIFIER || curClassifierIdx == multipleClassifierIdx )
+      {
+        m_calcClass2( classifier[curClassifierIdx], blkDst, Area(blkDst.pos().x, blkDst.pos().y, blkDst.width, blkDst.height), srcLuma, 2, classifier[0], curClassifierIdx, bits);
+      }
+    }
+#else
     m_calcClass1( classifier, blkDst, Area(blkDst.pos().x, blkDst.pos().y, blkDst.width, blkDst.height), 5, 0, 5, 5, bits, 2, mappingDir, laplacian );
+#endif
   }
 }
 #else
@@ -2265,6 +2409,14 @@ void AdaptiveLoopFilter::filterBlkCcAlf(const PelBuf &dstBuf, const CPelUnitBuf
         int offset1 = lumaStride;
         int offset2 = -lumaStride;
         int offset3 = 2 * lumaStride;
+#if JVET_X0071_LONGER_CCALF
+        int offset4 = -2 * lumaStride;
+        int offset5 =  3 * lumaStride;
+        int offset6 = -3 * lumaStride;
+        int offset7 = 4 * lumaStride;
+        int offset8 = -4 * lumaStride;
+#endif
+
         row <<= scaleY;
         col <<= scaleX;
         const Pel *srcCross = lumaPtr + col + row * lumaStride;
@@ -2294,6 +2446,36 @@ void AdaptiveLoopFilter::filterBlkCcAlf(const PelBuf &dstBuf, const CPelUnitBuf
 
           int sum = 0;
           const Pel currSrcCross = srcCross[offset0 + jj2];
+
+#if JVET_X0071_LONGER_CCALF
+          sum += filterCoeff[0] * (srcCross[offset8 + jj2] - currSrcCross);
+          sum += filterCoeff[1] * (srcCross[offset6 + jj2] - currSrcCross);
+          sum += filterCoeff[2] * (srcCross[offset4 + jj2] - currSrcCross);
+          sum += filterCoeff[3] * (srcCross[offset2 + jj2] - currSrcCross);
+
+          sum += filterCoeff[4] * (srcCross[offset0 + jj2 - 4] - currSrcCross);
+          sum += filterCoeff[5] * (srcCross[offset0 + jj2 - 3] - currSrcCross);
+          sum += filterCoeff[6] * (srcCross[offset0 + jj2 - 2] - currSrcCross);
+          sum += filterCoeff[7] * (srcCross[offset0 + jj2 - 1] - currSrcCross);
+          sum += filterCoeff[8] * (srcCross[offset0 + jj2 + 1] - currSrcCross);
+          sum += filterCoeff[9] * (srcCross[offset0 + jj2 + 2] - currSrcCross);
+          sum += filterCoeff[10] * (srcCross[offset0 + jj2 + 3] - currSrcCross);
+          sum += filterCoeff[11] * (srcCross[offset0 + jj2 + 4] - currSrcCross);
+
+          sum += filterCoeff[12] * (srcCross[offset1 + jj2 - 4] - currSrcCross);
+          sum += filterCoeff[13] * (srcCross[offset1 + jj2 - 3] - currSrcCross);
+          sum += filterCoeff[14] * (srcCross[offset1 + jj2 - 2] - currSrcCross);
+          sum += filterCoeff[15] * (srcCross[offset1 + jj2 - 1] - currSrcCross);
+          sum += filterCoeff[16] * (srcCross[offset1 + jj2 - 0] - currSrcCross);
+          sum += filterCoeff[17] * (srcCross[offset1 + jj2 + 1] - currSrcCross);
+          sum += filterCoeff[18] * (srcCross[offset1 + jj2 + 2] - currSrcCross);
+          sum += filterCoeff[19] * (srcCross[offset1 + jj2 + 3] - currSrcCross);
+          sum += filterCoeff[20] * (srcCross[offset1 + jj2 + 4] - currSrcCross);
+
+          sum += filterCoeff[21] * (srcCross[offset3 + jj2] - currSrcCross);
+          sum += filterCoeff[22] * (srcCross[offset5 + jj2] - currSrcCross);
+          sum += filterCoeff[23] * (srcCross[offset7 + jj2] - currSrcCross);
+#else
           sum += filterCoeff[0] * (srcCross[offset2 + jj2    ] - currSrcCross);
           sum += filterCoeff[1] * (srcCross[offset0 + jj2 - 1] - currSrcCross);
           sum += filterCoeff[2] * (srcCross[offset0 + jj2 + 1] - currSrcCross);
@@ -2301,7 +2483,7 @@ void AdaptiveLoopFilter::filterBlkCcAlf(const PelBuf &dstBuf, const CPelUnitBuf
           sum += filterCoeff[4] * (srcCross[offset1 + jj2    ] - currSrcCross);
           sum += filterCoeff[5] * (srcCross[offset1 + jj2 + 1] - currSrcCross);
           sum += filterCoeff[6] * (srcCross[offset3 + jj2    ] - currSrcCross);
-
+#endif
           sum = (sum + ((1 << m_scaleBits ) >> 1)) >> m_scaleBits;
           const int offset = 1 << clpRngs.comp[compId].bd >> 1;
           sum = ClipPel(sum + offset, clpRngs.comp[compId]) - offset;
diff --git a/source/Lib/CommonLib/AdaptiveLoopFilter.h b/source/Lib/CommonLib/AdaptiveLoopFilter.h
index bdcf249ce3c5911589a3af4ff48f4c7132633422..d20285652010f29261f72c605471273cc3de4315 100644
--- a/source/Lib/CommonLib/AdaptiveLoopFilter.h
+++ b/source/Lib/CommonLib/AdaptiveLoopFilter.h
@@ -120,20 +120,12 @@ public:
   static void calcClass(AlfClassifier **classifier, const Area &blkDst, const Area &cu, int dirWindSize, int classDir, int noDir, int noAct, int bitDepth, int subBlkSize, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX], uint32_t **laplacian[NUM_DIRECTIONS]);
   static void deriveClassificationLaplacianBig(const Area &curBlk, uint32_t **laplacian[NUM_DIRECTIONS]);
   static void deriveClassificationLaplacian(const CPelBuf &srcLuma, const Area &blkDst, const Area &blk, uint32_t **laplacian[NUM_DIRECTIONS]);
-  void deriveClassificationAndFixFilterResultsBlk( 
-    AlfClassifier **classifier,
-    Pel ***fixedFilterResults,
-    const CPelBuf &srcLuma,
-    const Area &blkDst,
-    const Area &blk,
-    const int bits,
-    CodingStructure& cs,
-    const ClpRng &clpRng,
-    const Pel clippingValues[4],
-    int qp, int qpIdx, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX],
-    uint32_t **laplacian[NUM_DIRECTIONS], 
-    const int classifierIdx
-  );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  void deriveClassificationAndFixFilterResultsBlk( AlfClassifier ***classifier, Pel ***fixedFilterResults, const CPelBuf &srcLuma, const Area &blkDst, const Area &blk, const int bits, CodingStructure& cs, const ClpRng &clpRng, const Pel clippingValues[4], int qp, int qpIdx, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX], uint32_t **laplacian[NUM_DIRECTIONS], const int classifierIdx, const int multipleClassifierIdx );
+  static void calcClassNew( AlfClassifier **classifier, const Area &blkDst, const Area &cu, const CPelBuf& srcLuma, int subBlkSize, AlfClassifier **classifier0, int classifierIdx, int bitDepth );
+#else
+  void deriveClassificationAndFixFilterResultsBlk( AlfClassifier **classifier, Pel ***fixedFilterResults, const CPelBuf &srcLuma, const Area &blkDst, const Area &blk, const int bits, CodingStructure& cs, const ClpRng &clpRng, const Pel clippingValues[4], int qp, int qpIdx, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX], uint32_t **laplacian[NUM_DIRECTIONS], const int classifierIdx );
+#endif
   template<AlfFilterType filtTypeCcAlf>
   static void filterBlkCcAlf(const PelBuf& dstBuf, const CPelUnitBuf& recSrc, const Area& blkDst, const Area& blkSrc, const ComponentID compId, const int16_t* filterCoeff, const ClpRngs& clpRngs, CodingStructure& cs);
 #else
@@ -141,15 +133,22 @@ public:
     template<AlfFilterType filtTypeCcAlf>
   static void filterBlkCcAlf(const PelBuf &dstBuf, const CPelUnitBuf &recSrc, const Area &blkDst, const Area &blkSrc, const ComponentID compId, const int16_t *filterCoeff, const ClpRngs &clpRngs, CodingStructure &cs, int vbCTUHeight, int vbPos);
 #endif
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  void deriveClassification( AlfClassifier*** classifier, const CPelBuf& srcLuma, const Area& blkDst, const Area& blk, CodingStructure &cs, const int classifierIdx, const int multipleClassifierIdx );
+#else
   void deriveClassification( AlfClassifier** classifier, const CPelBuf& srcLuma, const Area& blkDst, const Area& blk
 #if ALF_IMPROVEMENT
   , CodingStructure &cs, const int classifierIdx
 #endif
   );
+#endif
 
 #if ALF_IMPROVEMENT
   void(*m_calcClass0)(AlfClassifier **classifier, const Area &blkDst, const Area &cu, int dirWindSize, int classDir, int noDir, int noAct, int bitDepth, int subBlkSize, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX], uint32_t **laplacian[NUM_DIRECTIONS]);
   void(*m_calcClass1)(AlfClassifier **classifier, const Area &blkDst, const Area &cu, int dirWindSize, int classDir, int noDir, int noAct, int bitDepth, int subBlkSize, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX], uint32_t **laplacian[NUM_DIRECTIONS]);
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  void(*m_calcClass2)(AlfClassifier **classifier, const Area &blkDst, const Area &cu, const CPelBuf& srcLuma, int subBlkSize, AlfClassifier **classifier0, int classifierIdx, int bitDepth);
+#endif
   void(*m_deriveClassificationLaplacianBig)(const Area &curBlk, uint32_t **laplacian[NUM_DIRECTIONS]);
   void(*m_deriveClassificationLaplacian)(const CPelBuf &srcLuma, const Area &blkDst, const Area &blk, uint32_t **laplacian[NUM_DIRECTIONS]);
   void(*m_filter13x13Blk)(AlfClassifier **classifier, const CPelBuf &srcLuma, const Area& curBlk, Pel ***fixedFilterResults, int picWidth, const int fixedFiltInd, const short classIndFixed[NUM_CLASSES_FIX], int fixedFiltQpInd, int dirWindSize, const ClpRng &clpRng, const Pel clippingValues[4]);
@@ -235,7 +234,13 @@ protected:
   bool                         m_filterTypeTest[MAX_NUM_CHANNEL_TYPE][ALF_NUM_OF_FILTER_TYPES];
   int                          m_filterTypeToStatIndex[MAX_NUM_CHANNEL_TYPE][ALF_NUM_OF_FILTER_TYPES];
 #endif
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  AlfClassifier**              m_classifier[ALF_NUM_CLASSIFIER];
+  char                         m_classifierIdxApsLuma[ALF_CTB_MAX_NUM_APS][MAX_NUM_ALF_ALTERNATIVES_LUMA];
+  char                         m_classifierFinal[MAX_NUM_ALF_ALTERNATIVES_LUMA];
+#else
   AlfClassifier**              m_classifier;
+#endif
 #if ALF_IMPROVEMENT
   int                          m_numLumaAltAps[ALF_CTB_MAX_NUM_APS];
   short                        m_coeffApsLuma[ALF_CTB_MAX_NUM_APS][MAX_NUM_ALF_ALTERNATIVES_LUMA][MAX_NUM_ALF_LUMA_COEFF * MAX_NUM_ALF_CLASSES];
diff --git a/source/Lib/CommonLib/AlfParameters.h b/source/Lib/CommonLib/AlfParameters.h
index 883a31cabcae6770d6311ef4c67b3908638149e1..a45533c2b4085d974c9804676383a95cf1c0599b 100644
--- a/source/Lib/CommonLib/AlfParameters.h
+++ b/source/Lib/CommonLib/AlfParameters.h
@@ -195,9 +195,15 @@ struct AlfFilterShape
     else if( size == size_CC_ALF )
     {
       size = 4;
+#if JVET_X0071_LONGER_CCALF
+      filterLength = MAX_NUM_CC_ALF_CHROMA_COEFF;
+      numCoeff = MAX_NUM_CC_ALF_CHROMA_COEFF;
+      filterSize = MAX_NUM_CC_ALF_CHROMA_COEFF;
+#else
       filterLength = 8;
       numCoeff = 8;
       filterSize = 8;
+#endif
       filterType   = CC_ALF;
     }
     else
@@ -224,6 +230,9 @@ struct AlfParam
 {
   bool                         enabledFlag[MAX_NUM_COMPONENT];                          // alf_slice_enable_flag, alf_chroma_idc
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  char                         lumaClassifierIdx[MAX_NUM_ALF_ALTERNATIVES_LUMA];
+#endif
   AlfFilterType                filterType[MAX_NUM_CHANNEL_TYPE];
   bool                         nonLinearFlag[MAX_NUM_CHANNEL_TYPE][32]; // alf_[luma/chroma]_clip_flag
   int                          numAlternativesLuma;
@@ -267,6 +276,9 @@ struct AlfParam
   {
     std::memset( enabledFlag, false, sizeof( enabledFlag ) );
     std::memset( nonLinearFlag, false, sizeof( nonLinearFlag ) );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    std::memset( lumaClassifierIdx, 0, sizeof( lumaClassifierIdx ) );
+#endif
     std::memset( lumaCoeff, 0, sizeof( lumaCoeff ) );
     std::memset( lumaClipp, 0, sizeof( lumaClipp ) );
     numAlternativesChroma = 1;
@@ -292,6 +304,9 @@ struct AlfParam
   {
     std::memcpy( enabledFlag, src.enabledFlag, sizeof( enabledFlag ) );
     std::memcpy( nonLinearFlag, src.nonLinearFlag, sizeof( nonLinearFlag ) );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    std::memcpy( lumaClassifierIdx, src.lumaClassifierIdx, sizeof( lumaClassifierIdx ) );
+#endif
     std::memcpy( lumaCoeff, src.lumaCoeff, sizeof( lumaCoeff ) );
     std::memcpy( lumaClipp, src.lumaClipp, sizeof( lumaClipp ) );
     numAlternativesChroma = src.numAlternativesChroma;
@@ -327,6 +342,12 @@ struct AlfParam
     {
       return false;
     }
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    if( memcmp( lumaClassifierIdx, other.lumaClassifierIdx, sizeof( lumaClassifierIdx ) ) )
+    {
+      return false;
+    }
+#endif
 #endif
     if( memcmp( lumaCoeff, other.lumaCoeff, sizeof( lumaCoeff ) ) )
     {
diff --git a/source/Lib/CommonLib/BilateralFilter.cpp b/source/Lib/CommonLib/BilateralFilter.cpp
index 43f1268e10cca9e6ce8e851cbbce31b712d79e04..f36751c882e56124238b0ed118e94488b81b6d26 100755
--- a/source/Lib/CommonLib/BilateralFilter.cpp
+++ b/source/Lib/CommonLib/BilateralFilter.cpp
@@ -32,12 +32,12 @@
 */
 
 #include "BilateralFilter.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "Unit.h"
 #include "UnitTools.h"
 #endif
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include <tmmintrin.h>
 #include <smmintrin.h>
 #include <immintrin.h>
@@ -55,7 +55,7 @@ BilateralFilter::BilateralFilter()
   m_bilateralFilterDiamond5x5NoClip = blockBilateralFilterDiamond5x5NoClip;
 #endif
 
-#if ENABLE_SIMD_BILATERAL_FILTER
+#if ENABLE_SIMD_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER_ENABLE_SIMD
 #ifdef TARGET_SIMD_X86
   initBilateralFilterX86();
 #endif
@@ -73,7 +73,7 @@ void BilateralFilter::create()
 void BilateralFilter::destroy()
 {
 }
-
+#if JVET_V0094_BILATERAL_FILTER
 const char* BilateralFilter::getFilterLutParameters( const int size, const PredMode predMode, const int32_t qp, int& bfac )
 {
   if( size <= 4 )
@@ -119,7 +119,7 @@ const char* BilateralFilter::getFilterLutParameters( const int size, const PredM
 
   return m_wBIF[sqp - 17];
 }
-
+#endif
 #if JVET_W0066_CCSAO
 void BilateralFilter::blockBilateralFilterDiamond5x5NoClip(uint32_t uiWidth, uint32_t uiHeight, int16_t block[], int16_t blkFilt[], const ClpRng& clpRng, Pel* recPtr, int recStride, int iWidthExtSIMD, int bfac, int bif_round_add, int bif_round_shift, bool isRDO, const char* LUTrowPtr)
 {
@@ -587,7 +587,7 @@ void BilateralFilter::blockBilateralFilterDiamond5x5( uint32_t uiWidth, uint32_t
     }
   }
 }
-
+#if JVET_V0094_BILATERAL_FILTER
 void BilateralFilter::bilateralFilterRDOdiamond5x5(PelBuf& resiBuf, const CPelBuf& predBuf, PelBuf& recoBuf, int32_t qp, const CPelBuf& recIPredBuf, const ClpRng& clpRng, TransformUnit & currTU, bool useReco, bool doReshape, std::vector<Pel>& pLUT)
 {
   const unsigned uiWidth = predBuf.width;
@@ -796,8 +796,8 @@ void BilateralFilter::bilateralFilterRDOdiamond5x5(PelBuf& resiBuf, const CPelBu
     }
   }
 }
-
-#if JVET_W0066_CCSAO
+#endif
+#if JVET_W0066_CCSAO && JVET_V0094_BILATERAL_FILTER
 void BilateralFilter::bilateralFilterDiamond5x5NoClip(const CPelUnitBuf& src, PelUnitBuf& rec, int32_t qp, const ClpRng& clpRng, TransformUnit& currTU)
 {
   CompArea& compArea = currTU.block(COMPONENT_Y);
@@ -1031,7 +1031,7 @@ void BilateralFilter::bilateralFilterDiamond5x5NoClip(const CPelUnitBuf& src, Pe
   m_bilateralFilterDiamond5x5NoClip(uiWidth, uiHeight, tempblock, tempblockFiltered, clpRng, recPtr, recStride, iWidthExtSIMD, bfac, bif_round_add, bif_round_shift, false, LUTrowPtr);
 }
 #endif
-
+#if JVET_V0094_BILATERAL_FILTER
 void BilateralFilter::bilateralFilterDiamond5x5(const CPelUnitBuf& src, PelUnitBuf& rec, int32_t qp, const ClpRng& clpRng, TransformUnit & currTU)
 {
   CompArea &compArea = currTU.block(COMPONENT_Y);
@@ -1264,7 +1264,7 @@ void BilateralFilter::bilateralFilterDiamond5x5(const CPelUnitBuf& src, PelUnitB
   
   m_bilateralFilterDiamond5x5(uiWidth, uiHeight, tempblock, tempblockFiltered, clpRng, recPtr, recStride, iWidthExtSIMD, bfac, bif_round_add, bif_round_shift, false, LUTrowPtr );
 }
-
+#endif
 void BilateralFilter::clipNotBilaterallyFilteredBlocks(const CPelUnitBuf& src, PelUnitBuf& rec, const ClpRng& clpRng, TransformUnit & currTU)
 {
   PelUnitBuf myRecBuf = currTU.cs->getRecoBuf(currTU);
@@ -1335,7 +1335,7 @@ void copyBack(PelBuf &srcBuf, PelBuf &dstBuf)
     dstPtr += dstStride-srcBuf.width;
   }
 }
-
+#if JVET_V0094_BILATERAL_FILTER
 void BilateralFilter::bilateralFilterPicRDOperCTU(CodingStructure& cs, PelUnitBuf& src, BIFCabacEst* BifCABACEstimator)
 {
   // We must have already copied recobuf into src before running this
@@ -1454,7 +1454,28 @@ void BilateralFilter::bilateralFilterPicRDOperCTU(CodingStructure& cs, PelUnitBu
     // If no CTUs should be BIF-filtered, we need to restore all CTUs.
     // Note that this test must be done last since it is destroying all
     // of our filtered data.
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if(cs.pps->getUseCBIF())
+    {
+        for (int y = 0; y < pcv.heightInCtus; y++)
+        {
+            for (int x = 0; x < pcv.widthInCtus; x++)
+            {
+                UnitArea ctuArea(pcv.chrFormat, Area(x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth));
+                ctuArea = clipArea(ctuArea, *cs.slice->getPic());
+                PelBuf piRec = rec.subBuf(ctuArea).Y();
+                PelBuf piSrc = src.subBuf(ctuArea).Y();
+                copyBack(piSrc, piRec); // Copy unfiltered samples back to rec
+            }
+        }
+    }
+    else
+    {
+          rec.copyFrom(src);
+    }
+#else
     rec.copyFrom(src);
+#endif
   }
 
   if( bifParams.frmOn == 0 )
@@ -1466,5 +1487,1037 @@ void BilateralFilter::bilateralFilterPicRDOperCTU(CodingStructure& cs, PelUnitBu
     std::fill( bifParams.ctuOn.begin(), bifParams.ctuOn.end(), 1 );
   }
 }
+#endif
+
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+void BilateralFilter::bilateralFilterRDOdiamond5x5_chroma(PelBuf& resiBuf, const CPelBuf& predBuf, PelBuf& recoBuf, int32_t qp, const CPelBuf& recIPredBuf, const ClpRng& clpRng, TransformUnit & currTU, bool useReco, bool isCb)
+{
+    const unsigned uiWidth = predBuf.width;
+    const unsigned uiHeight = predBuf.height;
+
+    int bfac = 1;
+    int bif_round_add = (BIF_ROUND_ADD) >> (currTU.cs->pps->getCBIFStrength());
+    int bif_round_shift = ( BIF_ROUND_SHIFT ) -(currTU.cs->pps->getCBIFStrength());
+
+    ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+    int width_for_strength = currTU.blocks[compID].width;
+    int height_for_strength = currTU.blocks[compID].height;
+
+    if(currTU.blocks[COMPONENT_Y].valid())
+    {
+        width_for_strength = currTU.blocks[COMPONENT_Y].width;
+        height_for_strength = currTU.blocks[COMPONENT_Y].height;
+    }
+
+    const char* LUTrowPtr = getFilterLutParameters_chroma( std::min( uiWidth, uiHeight ), currTU.cu->predMode, qp + currTU.cs->pps->getCBIFQPOffset(), bfac, width_for_strength, height_for_strength, currTU.blocks[COMPONENT_Y].valid());
+
+    const unsigned uiPredStride = predBuf.stride;
+    const unsigned uiStrideRes = resiBuf.stride;
+    const unsigned uiRecStride = recoBuf.stride;
+    const Pel *piPred = predBuf.buf;
+    Pel *piResi = resiBuf.buf;
+    Pel *piReco = recoBuf.buf;
+
+    const Pel *piPredTemp = piPred;
+    Pel *piResiTemp = piResi;
+    Pel *piRecoTemp = piReco;
+    // Reco = Pred + Resi
+
+    Pel *tempBlockPtr;
+
+    uint32_t   uiWidthExt = uiWidth + (NUMBER_PADDED_SAMPLES << 1);
+    uint32_t   uiHeightExt = uiHeight + (NUMBER_PADDED_SAMPLES << 1);
+
+    int iWidthExtSIMD = uiWidthExt;
+    if( uiWidth < 8 )
+    {
+        iWidthExtSIMD = 8 + (NUMBER_PADDED_SAMPLES << 1);
+    }
+
+    memset(tempblock, 0, iWidthExtSIMD*uiHeightExt * sizeof(short));
+    tempBlockPtr = tempblock + (NUMBER_PADDED_SAMPLES)* iWidthExtSIMD + NUMBER_PADDED_SAMPLES;
+
+    // Clip and move block to temporary block
+    if (useReco)
+    {
+        for (uint32_t uiY = 0; uiY < uiHeight; ++uiY)
+        {
+            std::memcpy(tempBlockPtr, piReco, uiWidth * sizeof(Pel));
+            piReco += uiRecStride;
+            tempBlockPtr += iWidthExtSIMD;
+        }
+        piReco = piRecoTemp;
+    }
+    else
+    {
+        for (uint32_t uiY = 0; uiY < uiHeight; ++uiY)
+        {
+            for (uint32_t uiX = 0; uiX < uiWidth; ++uiX)
+            {
+                tempBlockPtr[uiX] = ClipPel(piPred[uiX] + piResi[uiX], clpRng);
+            }
+            piPred += uiPredStride;
+            piResi += uiStrideRes;
+            piReco += uiRecStride;
+            tempBlockPtr += iWidthExtSIMD;
+        }
+    }
+
+    piPred = piPredTemp;
+    piResi = piResiTemp;
+    piReco = piRecoTemp;
+
+    //USE chroma info to decided
+    const unsigned uiRecIPredStride = recIPredBuf.stride;
+    const Pel *piRecIPred = recIPredBuf.buf;
+
+    for (int yy = 1; yy< uiHeightExt -1 ; yy++)
+    {
+        tempblock[yy*iWidthExtSIMD + 1] = tempblock[yy*iWidthExtSIMD + 2];
+        tempblock[yy*iWidthExtSIMD + uiWidthExt - 2] = tempblock[yy*iWidthExtSIMD + uiWidthExt - 3];
+    }
+    for (int xx = 1; xx< uiWidthExt - 1; xx++)
+    {
+        tempblock[1 * iWidthExtSIMD + xx] = tempblock[2 * iWidthExtSIMD + xx];
+        tempblock[(uiHeightExt - 2)*iWidthExtSIMD + xx] = tempblock[(uiHeightExt - 3)*iWidthExtSIMD + xx];
+    }
+
+    bool subTuVer = currTU.chromaPos().x > currTU.cu->chromaPos().x ? true : false;
+    bool subTuHor = currTU.chromaPos().y > currTU.cu->chromaPos().y ? true : false;
+
+    uint32_t CTUsize_chroma = currTU.cs->slice->getSPS()->getCTUSize() >> 1;
+
+    bool isCTUboundary = currTU.chromaPos().y % CTUsize_chroma == 0;
+
+    bool topAvailable = (currTU.chromaPos().y - 2 >= 0) && (currTU.chromaPos().y == currTU.cu->chromaPos().y);
+
+    topAvailable &= !isCTUboundary;
+
+    bool leftAvailable = (currTU.chromaPos().x - 2 >= 0) && (currTU.chromaPos().x == currTU.cu->chromaPos().x);
+
+    //if not 420, then don't use rec for padding
+    if(currTU.cu->chromaFormat != CHROMA_420){
+        subTuHor = false;
+        subTuVer = false;
+        leftAvailable = false;
+        topAvailable = false;
+    }
+
+    if(topAvailable || leftAvailable || subTuHor || subTuVer)
+    {
+        const CompArea &area = isCb ? currTU.blocks[COMPONENT_Cb] : currTU.blocks[COMPONENT_Cr];
+        CodingStructure &cs = *currTU.cs;
+
+        if (topAvailable && leftAvailable)
+        {
+            // top left pixels
+            tempblock[iWidthExtSIMD + 1] = *(piRecIPred - (uiRecIPredStride)-1);
+        }
+        // top row
+        if (topAvailable)
+        {
+            for (int blockx = 0; blockx < area.width; blockx += 1)
+            {
+                // copy 4 pixels one line above block from block to blockx + 3
+                std::copy(piRecIPred - (uiRecIPredStride)+blockx, piRecIPred - (uiRecIPredStride)+blockx + 1, tempblock + 2 + iWidthExtSIMD + blockx);
+            }
+        }
+        else if (subTuHor)
+        {
+            const CompArea &prevHalfArea = isCb ? currTU.prev->blocks[COMPONENT_Cb] : currTU.prev->blocks[COMPONENT_Cr];
+            CPelBuf earlierHalfBuf = cs.getPredBuf(prevHalfArea);
+            earlierHalfBuf = cs.getRecoBuf(prevHalfArea);
+            const unsigned earlierStride = earlierHalfBuf.stride;
+            const Pel *earlierPel = earlierHalfBuf.buf + (currTU.prev->chromaSize().height - 1)*earlierStride;
+            std::copy(earlierPel, earlierPel + area.width, tempblock + 2 + iWidthExtSIMD);
+            std::copy(earlierPel - earlierStride, earlierPel - earlierStride + area.width, tempblock + 2);
+        }
+        // left column
+        if (leftAvailable)
+        {
+            for (int blocky = 0; blocky < area.height; blocky += 1)
+            {
+                tempblock[(iWidthExtSIMD << 1) + (blocky + 0) * iWidthExtSIMD + 1] = *(piRecIPred + (blocky + 0)*uiRecIPredStride - 1); // 1 pel out
+            }
+        }
+        else if (subTuVer)
+        {
+            const CompArea &prevHalfArea = isCb ? currTU.prev->blocks[COMPONENT_Cb] : currTU.prev->blocks[COMPONENT_Cr];
+            CPelBuf earlierHalfBuf = cs.getPredBuf(prevHalfArea);
+            earlierHalfBuf = cs.getRecoBuf(prevHalfArea);
+            const unsigned earlierStride = earlierHalfBuf.stride;
+            const Pel *earlierPel = earlierHalfBuf.buf + (currTU.prev->chromaSize().width - 1); // second to last pixel of first row of previous block
+            for (int yy = 0; yy < area.height; yy++)
+            {
+                tempblock[(iWidthExtSIMD << 1) + yy * iWidthExtSIMD + 1] = *(earlierPel + yy*earlierStride + 0);
+            }
+        }
+    }
+
+    // Sloppy copying of outer layer
+    for(int yy = 0; yy < uiHeight+2; yy++)
+    {
+        tempblock[iWidthExtSIMD + yy*iWidthExtSIMD] = tempblock[iWidthExtSIMD + yy*iWidthExtSIMD + 1];
+        tempblock[iWidthExtSIMD + uiWidthExt - 1 + yy*iWidthExtSIMD] = tempblock[iWidthExtSIMD + uiWidthExt - 2 + yy*iWidthExtSIMD];
+    }
+    std::copy(tempblock + iWidthExtSIMD, tempblock + iWidthExtSIMD + uiWidthExt, tempblock);
+    std::copy(tempblock  + iWidthExtSIMD*(uiHeightExt-2), tempblock  + iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt, tempblock + iWidthExtSIMD*(uiHeightExt-1));
+
+    m_bilateralFilterDiamond5x5(uiWidth, uiHeight, tempblock, tempblockFiltered, clpRng, piReco, uiRecStride, iWidthExtSIMD, bfac, bif_round_add, bif_round_shift, true, LUTrowPtr );
+
+    if (!useReco)
+    {
+        // need to be performed if residual  is used
+        // Resi' = Reco' - Pred
+        for (uint32_t uiY = 0; uiY < uiHeight; ++uiY)
+        {
+            for (uint32_t uiX = 0; uiX < uiWidth; ++uiX)
+            {
+                piResi[uiX] = piReco[uiX] - piPred[uiX];
+            }
+            piPred += uiPredStride;
+            piResi += uiStrideRes;
+            piReco += uiRecStride;
+        }
+    }
+}
+
+
+
+
+void BilateralFilter::bilateralFilterPicRDOperCTU_chroma(CodingStructure& cs, PelUnitBuf& src, CBIFCabacEst* CBifCABACEstimator, bool isCb)
+{
+
+    // We must have already copied recobuf into src before running this
+    // such as src.copyFrom(rec);
+    const PreCalcValues& pcv = *cs.pcv;
+
+    PelUnitBuf rec = cs.getRecoBuf();
+
+    double frameMSEnoBIF = 0;
+    double frameMSEallBIF = 0;
+    double frameMSEswitchBIF = 0;
+    CBifParams& CBifParams = cs.picture->getCBifParam();
+    int ctuIdx = 0;
+
+    for (int y = 0; y < pcv.heightInCtus; y++)
+    {
+        for (int x = 0; x < pcv.widthInCtus; x++)
+        {
+            UnitArea ctuArea(pcv.chrFormat, Area(x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth));
+
+            ctuArea = clipArea(ctuArea, *cs.slice->getPic());
+            PelBuf piOrg = isCb ? cs.getOrgBuf(ctuArea).Cb() : cs.getOrgBuf(ctuArea).Cr();
+            PelBuf piSrc = isCb ? src.subBuf(ctuArea).Cb() : src.subBuf(ctuArea).Cr();
+
+            double MSEnoBIF = getDist(piSrc, piOrg);
+
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+            bool BIF_chroma = false;
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, ctuArea, CType), CType))
+            {
+                bool chroma_valid = isCb ? currCU.Cb().valid() : currCU.Cr().valid();
+                if(!chroma_valid){
+                    continue;
+                }
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+                    BIF_chroma = false;
+                    if(!isDualTree)
+                    {
+                        TU_VALID = isCb ? currTU.blocks[COMPONENT_Cb].valid() : currTU.blocks[COMPONENT_Cr].valid();
+                        TU_CBF = false;
+
+                        if(TU_VALID)
+                        {
+                            TU_CBF = (isCb ? TU::getCbf(currTU, COMPONENT_Cb) : TU::getCbf(currTU, COMPONENT_Cr));
+                        }
+                        BIF_chroma =  ((TU_CBF || isInter == false) && (currTU.cu->qp > 17) && (TU_VALID));
+                    }
+                    else
+                    {
+                        TU_CBF = (isCb ? TU::getCbf(currTU, COMPONENT_Cb) : TU::getCbf(currTU, COMPONENT_Cr));
+                        BIF_chroma = (TU_CBF || isInter == false) && (currTU.cu->qp > 17);
+                    }
+                    if(BIF_chroma)
+                    {
+                        bilateralFilterDiamond5x5_chroma(src, rec, currTU.cu->qp, isCb ? cs.slice->clpRng(COMPONENT_Cb) : cs.slice->clpRng(COMPONENT_Cr), currTU, isCb);
+                    }
+                }
+            }
+            PelBuf piRec = isCb ? rec.subBuf(ctuArea).Cb() : rec.subBuf(ctuArea).Cr();
+            double MSEafterBIF = getDist(piRec, piOrg);
+            frameMSEnoBIF += MSEnoBIF;
+            frameMSEallBIF += MSEafterBIF;
+            if(MSEnoBIF<MSEafterBIF)
+            {
+                frameMSEswitchBIF += MSEnoBIF;
+                if(isCb){
+                    CBifParams.ctuOn_Cb[ctuIdx] = 0;
+                }
+                else{
+                    CBifParams.ctuOn_Cr[ctuIdx] = 0;
+                }
+            }
+            else
+            {
+                frameMSEswitchBIF += MSEafterBIF;
+                if(isCb){
+                    CBifParams.ctuOn_Cb[ctuIdx] = 1;
+                }
+                else{
+                    CBifParams.ctuOn_Cr[ctuIdx] = 1;
+                }
+            }
+            ctuIdx++;
+        }
+    }
+
+    double lambda = cs.picture->slices[0]->getLambdas()[isCb ? 1 : 2];
+    double costAllCTUsBIF  = frameMSEallBIF + lambda * 1;      // To turn everything on, only slice_bif_all_ctb_enabled_flag = 1, so one bit.
+    double costNoCTUsBIF = frameMSEnoBIF + lambda * 2;         // To turn everything off, slice_bif_all_ctb_enabled = 0 && slice_bif_enabled_flag = 0, so two bits.
+    double costSwitchCTUsBIF;
+    // Does CABAC estimation instead
+
+    const double FracBitsScale = 1.0 / double(1 << SCALE_BITS);
+    if(isCb)
+    {
+        CBifParams.frmOn_Cb = 1;
+    }
+    else
+    {
+        CBifParams.frmOn_Cr = 1;
+    }
+    if(isCb)
+    {
+        CBifParams.allCtuOn_Cb = 0;
+    }
+    else{
+        CBifParams.allCtuOn_Cr = 0;
+    }
+    double ctuSwitchBits = isCb ? FracBitsScale*CBifCABACEstimator->getBits_Cb(*cs.slice, CBifParams) : FracBitsScale*CBifCABACEstimator->getBits_Cr(*cs.slice, CBifParams);
+    costSwitchCTUsBIF  = frameMSEswitchBIF  + lambda * ctuSwitchBits;
+
+    double bestCost = MAX_DOUBLE;
+    if (costAllCTUsBIF < bestCost)
+    {
+        // If everything should be BIF-filtered, we do not need to change any of the samples,
+        // since they are already filtered.
+        bestCost = costAllCTUsBIF;
+        if(isCb)
+        {
+            CBifParams.frmOn_Cb = 1;
+        }
+        else
+        {
+            CBifParams.frmOn_Cr = 1;
+        }
+        if(isCb)
+        {
+            CBifParams.allCtuOn_Cb = 1;
+        }
+        else{
+            CBifParams.allCtuOn_Cr = 1;
+        }
+    }
+    if (costSwitchCTUsBIF < bestCost)
+    {
+        bestCost = costSwitchCTUsBIF;
+        if(isCb)
+        {
+            CBifParams.frmOn_Cb = 1;
+        }
+        else
+        {
+            CBifParams.frmOn_Cr = 1;
+        }
+        if(isCb)
+        {
+            CBifParams.allCtuOn_Cb = 0;
+        }
+        else{
+            CBifParams.allCtuOn_Cr = 0;
+        }
+        // If only some CTUs should be BIF-filtered, we need to restore the ones
+        // that should not be filtered. This test must be done before the above one
+        // since it is partly destroying our filtered data.
+        ctuIdx = 0;
+        for (int y = 0; y < pcv.heightInCtus; y++)
+        {
+            for (int x = 0; x < pcv.widthInCtus; x++)
+            {
+                UnitArea ctuArea(pcv.chrFormat, Area(x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth));
+                ctuArea = clipArea(ctuArea, *cs.slice->getPic());
+                PelBuf piRec = isCb ? rec.subBuf(ctuArea).Cb() : rec.subBuf(ctuArea).Cr();
+                PelBuf piSrc = isCb ? src.subBuf(ctuArea).Cb() : src.subBuf(ctuArea).Cr();
+                bool isCTUon = isCb ? CBifParams.ctuOn_Cb[ctuIdx] : CBifParams.ctuOn_Cr[ctuIdx];
+                if( isCTUon == 0){
+                    copyBack(piSrc, piRec); // Copy unfiltered samples back to rec
+                }
+                ctuIdx++;
+            }
+        }
+    }
+
+    if (costNoCTUsBIF < bestCost)
+    {
+        bestCost = costNoCTUsBIF;
+        if(isCb)
+        {
+            CBifParams.frmOn_Cb = 0;
+        }
+        else
+        {
+            CBifParams.frmOn_Cr = 0;
+        }
+        if(isCb)
+        {
+            CBifParams.allCtuOn_Cb = 0;
+        }
+        else
+        {
+            CBifParams.allCtuOn_Cr = 0;
+        }
+
+        for (int y = 0; y < pcv.heightInCtus; y++)
+        {
+            for (int x = 0; x < pcv.widthInCtus; x++)
+            {
+                UnitArea ctuArea(pcv.chrFormat, Area(x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth));
+                ctuArea = clipArea(ctuArea, *cs.slice->getPic());
+                PelBuf piRec = isCb ? rec.subBuf(ctuArea).Cb() : rec.subBuf(ctuArea).Cr();
+                PelBuf piSrc = isCb ? src.subBuf(ctuArea).Cb() : src.subBuf(ctuArea).Cr();
+                copyBack(piSrc, piRec); // Copy unfiltered samples back to rec
+            }
+        }
+    }
+
+    if(isCb){
+        if (CBifParams.frmOn_Cb == 0)
+        {
+            std::fill(CBifParams.ctuOn_Cb.begin(), CBifParams.ctuOn_Cb.end(), 0);
+        }
+        else if (CBifParams.allCtuOn_Cb)
+        {
+            std::fill(CBifParams.ctuOn_Cb.begin(), CBifParams.ctuOn_Cb.end(), 1);
+        }
+    }
+    else{
+        if (CBifParams.frmOn_Cr == 0)
+        {
+            std::fill(CBifParams.ctuOn_Cr.begin(), CBifParams.ctuOn_Cr.end(), 0);
+        }
+        else if (CBifParams.allCtuOn_Cr)
+        {
+            std::fill(CBifParams.ctuOn_Cr.begin(), CBifParams.ctuOn_Cr.end(), 1);
+        }
+    }
+
+}
+
+#if JVET_W0066_CCSAO
+void BilateralFilter::bilateralFilterDiamond5x5NoClip_chroma(const CPelUnitBuf& src, PelUnitBuf& rec, int32_t qp, const ClpRng& clpRng, TransformUnit & currTU, bool isCb)
+{
+
+    ComponentID compID = isCb ? COMPONENT_Cb :COMPONENT_Cr;
+    CompArea &compArea = currTU.block(compID);
+
+    const unsigned uiWidth = compArea.width;
+    const unsigned uiHeight = compArea.height;
+
+    int srcStride = src.get(compID).stride;
+    const Pel *srcPtr = src.get(compID).bufAt(compArea);
+    const Pel *srcPtrTemp = srcPtr;
+
+    int recStride = rec.get(compID).stride;
+    Pel *recPtr = rec.get(compID).bufAt(compArea);
+
+    int bfac = 1;
+    int bif_round_add = (BIF_ROUND_ADD) >> (currTU.cs->pps->getCBIFStrength());
+    int bif_round_shift = (BIF_ROUND_SHIFT) - (currTU.cs->pps->getCBIFStrength());
+
+    int width_for_strength = currTU.blocks[compID].width;
+    int height_for_strength = currTU.blocks[compID].height;
+
+    if(currTU.blocks[COMPONENT_Y].valid())
+    {
+        width_for_strength = currTU.blocks[COMPONENT_Y].width;
+        height_for_strength = currTU.blocks[COMPONENT_Y].height;
+    }
+
+    const char* LUTrowPtr = getFilterLutParameters_chroma( std::min( uiWidth, uiHeight ), currTU.cu->predMode, qp + currTU.cs->pps->getCBIFQPOffset(), bfac, width_for_strength, height_for_strength, currTU.blocks[COMPONENT_Y].valid());
+
+    uint32_t   uiWidthExt = uiWidth + (NUMBER_PADDED_SAMPLES << 1);
+    uint32_t   uiHeightExt = uiHeight + (NUMBER_PADDED_SAMPLES << 1);
+
+    int iWidthExtSIMD = uiWidthExt;
+    if( uiWidth < 8 )
+    {
+        iWidthExtSIMD = 8 + (NUMBER_PADDED_SAMPLES << 1);
+    }
+
+    Pel *tempBlockPtr;
+
+    memset(tempblock, 0, iWidthExtSIMD*uiHeightExt * sizeof(short));
+
+    tempBlockPtr = tempblock + (NUMBER_PADDED_SAMPLES)* iWidthExtSIMD + NUMBER_PADDED_SAMPLES;
+
+    //// Move block to temporary block
+    for (uint32_t uiY = 0; uiY < uiHeight; ++uiY)
+    {
+        std::memcpy(tempBlockPtr, srcPtr, uiWidth * sizeof(Pel));
+        srcPtr += srcStride;
+        tempBlockPtr += iWidthExtSIMD;
+    }
+    srcPtr = srcPtrTemp;
+
+    const CompArea &myArea = isCb ? currTU.blocks[COMPONENT_Cb] : currTU.blocks[COMPONENT_Cr];
+
+    bool topAltAvailable = myArea.y - 2 >= 0;
+    bool leftAltAvailable = myArea.x - 2 >= 0;
+
+//    uint32_t chroma_pic_width = currTU.cu->slice->getSPS()->getMaxPicWidthInLumaSamples() >> 1;
+//    uint32_t chroma_pic_height = currTU.cu->slice->getSPS()->getMaxPicHeightInLumaSamples() >> 1;
+    int scaleX  = getComponentScaleX(compID, currTU.cu->cs->pcv->chrFormat);
+    int scaleY  = getComponentScaleY(compID, currTU.cu->cs->pcv->chrFormat);
+    uint32_t chroma_pic_width = currTU.cu->slice->getPPS()->getPicWidthInLumaSamples() >> scaleX;
+    uint32_t chroma_pic_height = currTU.cu->slice->getPPS()->getPicHeightInLumaSamples() >> scaleY;
+
+    bool bottomAltAvailable = myArea.y + myArea.height + 1 < chroma_pic_height;
+    bool rightAltAvailable = myArea.x + myArea.width + 1 < chroma_pic_width;
+
+    bool allAvail = topAltAvailable && bottomAltAvailable && leftAltAvailable && rightAltAvailable;
+
+    //if not 420, then don't use rec for padding
+    if(currTU.cu->chromaFormat != CHROMA_420)
+    {
+        topAltAvailable = false;
+        bottomAltAvailable = false;
+        leftAltAvailable = false;
+        rightAltAvailable = false;
+        allAvail = false;
+    }
+
+    if(allAvail)
+    {
+        // set pointer two rows up and two pixels to the left from the start of the block
+        tempBlockPtr = tempblock;
+        // same with image data
+        srcPtr = srcPtr - 2*srcStride - 2;
+        // Move block to temporary block
+        // Check if the block a the top block of a CTU.
+        uint32_t CTUsize_chroma = currTU.cs->slice->getSPS()->getCTUSize() >> 1;
+        bool isCTUboundary = myArea.y % CTUsize_chroma == 0;
+
+        if(isCTUboundary)
+        {
+            // The samples two lines up are out of bounds. (One line above the CTU is OK, since SAO uses that line.)
+            // Hence the top line of tempblock is unavailable if the block is the top block of a CTU.
+            // Therefore, copy samples from one line up instead of from two lines up by updating srcPtr *before* copy.
+            srcPtr += srcStride;
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+        }
+        else
+        {
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+            srcPtr += srcStride;
+        }
+        tempBlockPtr += iWidthExtSIMD;
+        // Copy samples that are not out of bounds.
+        for (uint32_t uiY = 1; uiY < uiHeightExt-1; ++uiY)
+        {
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+            srcPtr += srcStride;
+            tempBlockPtr += iWidthExtSIMD;
+        }
+        // Check if the block is a bottom block of a CTU.
+        isCTUboundary = (myArea.y + uiHeight) % CTUsize_chroma == 0;
+        if(isCTUboundary)
+        {
+            // The samples two lines down are out of bounds. (One line below the CTU is OK, since SAO uses that line.)
+            // Hence the bottom line of tempblock is unavailable if the block at the bottom of a CTU.
+            // Therefore, copy samples from the second to last line instead of the last line by subtracting srcPtr before copy.
+            srcPtr -= srcStride;
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+        }
+        else
+        {
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+        }
+    }
+    else
+    {
+        tempBlockPtr = tempblock + (NUMBER_PADDED_SAMPLES)* iWidthExtSIMD + NUMBER_PADDED_SAMPLES;
+        // Move block to temporary block
+        for (uint32_t uiY = 0; uiY < uiHeight; ++uiY)
+        {
+            std::memcpy(tempBlockPtr, srcPtr, uiWidth * sizeof(Pel));
+            srcPtr += srcStride;
+            tempBlockPtr += iWidthExtSIMD;
+        }
+        srcPtr = srcPtrTemp;
+        if(topAltAvailable)
+        {
+            std::copy(srcPtr - 2*srcStride, srcPtr - 2*srcStride + uiWidth, tempblock + 2);
+            std::copy(srcPtr - srcStride, srcPtr - srcStride + uiWidth, tempblock + iWidthExtSIMD + 2);
+        }
+        if(bottomAltAvailable)
+        {
+            std::copy(srcPtr + (uiHeight+1)*srcStride, srcPtr +(uiHeight+1)*srcStride + uiWidth, tempblock + (uiHeightExt-1)*iWidthExtSIMD + 2);
+            std::copy(srcPtr + uiHeight*srcStride, srcPtr +uiHeight*srcStride + uiWidth, tempblock + (uiHeightExt-2)*iWidthExtSIMD + 2);
+        }
+        if(leftAltAvailable)
+        {
+            for(int yy = 0; yy<uiHeight; yy++)
+            {
+                tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 0] = *(srcPtr + yy*srcStride -2);
+                tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 1] = *(srcPtr + yy*srcStride -1);
+            }
+        }
+        if(rightAltAvailable)
+        {
+            for(int yy = 0; yy<uiHeight; yy++)
+            {
+                tempblock[(iWidthExtSIMD<<1) + uiWidthExt-1 + yy*iWidthExtSIMD] = *(srcPtr + uiWidth + yy*srcStride + 1);
+                tempblock[(iWidthExtSIMD<<1) + uiWidthExt-2 + yy*iWidthExtSIMD] = *(srcPtr + uiWidth + yy*srcStride);
+            }
+        }
+        // if not all available, copy from inside tempbuffer
+        if(!topAltAvailable)
+        {
+            std::copy(tempblock + iWidthExtSIMD*2 + 2, tempblock + iWidthExtSIMD*2 + 2 + uiWidth, tempblock + 2);
+            std::copy(tempblock + iWidthExtSIMD*2 + 2, tempblock + iWidthExtSIMD*2 + 2 + uiWidth, tempblock + iWidthExtSIMD + 2);
+        }
+        if(!bottomAltAvailable)
+        {
+            std::copy(tempblock + (uiHeightExt-3)*iWidthExtSIMD + 2, tempblock + (uiHeightExt-3)*iWidthExtSIMD + 2 + uiWidth, tempblock + (uiHeightExt-2)*iWidthExtSIMD + 2);
+            std::copy(tempblock + (uiHeightExt-3)*iWidthExtSIMD + 2, tempblock + (uiHeightExt-3)*iWidthExtSIMD + 2 + uiWidth, tempblock + (uiHeightExt-1)*iWidthExtSIMD + 2);
+        }
+        if(!leftAltAvailable)
+        {
+            for(int yy = 0; yy<uiHeight; yy++)
+            {
+                tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 0] = tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 2];
+                tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 1] = tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 2];
+            }
+        }
+        if(!rightAltAvailable)
+        {
+            for(int yy = 0; yy<uiHeight; yy++)
+            {
+                tempblock[(iWidthExtSIMD<<1) + uiWidthExt-2 + yy*iWidthExtSIMD] = tempblock[(iWidthExtSIMD<<1) + uiWidthExt-2 + yy*iWidthExtSIMD - 1];
+                tempblock[(iWidthExtSIMD<<1) + uiWidthExt-1 + yy*iWidthExtSIMD] = tempblock[(iWidthExtSIMD<<1) + uiWidthExt-2 + yy*iWidthExtSIMD - 1];
+            }
+        }
+        // All sides are available, easy to just copy corners also.
+        if(topAltAvailable && leftAltAvailable)
+        {
+            tempblock[0] = *(srcPtr - 2*srcStride -2);                                                // a     top left corner
+            tempblock[1] = *(srcPtr - 2*srcStride -1);                                                // b     a b|x x
+            tempblock[iWidthExtSIMD + 0] = *(srcPtr - srcStride -2);                                  // c     c d|x x
+            tempblock[iWidthExtSIMD + 1] = *(srcPtr - srcStride -1);                                  // d     -------
+        }
+        else
+        {
+            tempblock[0] = tempblock[iWidthExtSIMD*2 + 2];                                            // extend top left
+            tempblock[1] = tempblock[iWidthExtSIMD*2 + 2];                                            // extend top left
+            tempblock[iWidthExtSIMD + 0] = tempblock[iWidthExtSIMD*2 + 2];                            // extend top left
+            tempblock[iWidthExtSIMD + 1] = tempblock[iWidthExtSIMD*2 + 2];                            // extend top left
+        }
+        if(topAltAvailable && rightAltAvailable)
+        {
+            tempblock[iWidthExtSIMD - 2] = *(srcPtr - 2*srcStride + uiWidth);                         // a
+            tempblock[iWidthExtSIMD - 1] = *(srcPtr - 2*srcStride + uiWidth + 1);                     // b
+            tempblock[iWidthExtSIMD + uiWidthExt - 2] = *(srcPtr - srcStride + uiWidth);              // c
+            tempblock[iWidthExtSIMD + uiWidthExt - 1] = *(srcPtr - srcStride + uiWidth + 1);          // d
+        }
+        else
+        {
+            tempblock[iWidthExtSIMD - 2] = tempblock[iWidthExtSIMD*2 + uiWidthExt - 3];               // extend top right
+            tempblock[iWidthExtSIMD - 1] = tempblock[iWidthExtSIMD*2 + uiWidthExt - 3];               // extend top right
+            tempblock[iWidthExtSIMD + uiWidthExt - 2] = tempblock[iWidthExtSIMD*2 + uiWidthExt - 3];  // extend top right
+            tempblock[iWidthExtSIMD + uiWidthExt - 1] = tempblock[iWidthExtSIMD*2 + uiWidthExt - 3];  // extend top right
+        }
+        if(bottomAltAvailable && leftAltAvailable)
+        {
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + 0] = *(srcPtr + uiHeight*srcStride -2);          // a
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + 1] = *(srcPtr + uiHeight*srcStride -1);          // b
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + 0] = *(srcPtr + (uiHeight+1)*srcStride -2);      // c
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + 1] = *(srcPtr + (uiHeight+1)*srcStride -1);      // d
+        }
+        else
+        {
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + 0] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + 2];  // bot avail: mirror left/right
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + 1] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + 2];  // bot avail: mirror left/right
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + 0] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + 2];  // bot avail: mirror left/right
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + 1] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + 2];  // bot avail: mirror left/right
+        }
+        if(bottomAltAvailable && rightAltAvailable)
+        {
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt - 2] = *(srcPtr + uiHeight*srcStride + uiWidth);                // a
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt - 1] = *(srcPtr + uiHeight*srcStride + uiWidth + 1);            // b
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + uiWidthExt - 2] = *(srcPtr + (uiHeight+1)*srcStride + uiWidth);            // c
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + uiWidthExt - 1] = *(srcPtr + (uiHeight+1)*srcStride + uiWidth + 1);        // d
+        }
+        else
+        {
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt - 2] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + uiWidthExt - 3];
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt - 1] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + uiWidthExt - 3];
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + uiWidthExt - 2] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + uiWidthExt - 3];
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + uiWidthExt - 1] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + uiWidthExt - 3];
+        }
+    }
+
+    m_bilateralFilterDiamond5x5NoClip(uiWidth, uiHeight, tempblock, tempblockFiltered, clpRng, recPtr, recStride, iWidthExtSIMD, bfac, bif_round_add, bif_round_shift, false, LUTrowPtr );
+}
+#endif //BIFchroma no clip
+
+void BilateralFilter::bilateralFilterDiamond5x5_chroma(const CPelUnitBuf& src, PelUnitBuf& rec, int32_t qp, const ClpRng& clpRng, TransformUnit & currTU, bool isCb)
+{
+
+    ComponentID compID = isCb ? COMPONENT_Cb :COMPONENT_Cr;
+    CompArea &compArea = currTU.block(compID);
+
+    const unsigned uiWidth = compArea.width;
+    const unsigned uiHeight = compArea.height;
+
+    int srcStride = src.get(compID).stride;
+    const Pel *srcPtr = src.get(compID).bufAt(compArea);
+    const Pel *srcPtrTemp = srcPtr;
+
+    int recStride = rec.get(compID).stride;
+    Pel *recPtr = rec.get(compID).bufAt(compArea);
+
+    int bfac = 1;
+    int bif_round_add = (BIF_ROUND_ADD) >> (currTU.cs->pps->getCBIFStrength());
+    int bif_round_shift = (BIF_ROUND_SHIFT) - (currTU.cs->pps->getCBIFStrength());
+
+    int width_for_strength = currTU.blocks[compID].width;
+    int height_for_strength = currTU.blocks[compID].height;
+
+    if(currTU.blocks[COMPONENT_Y].valid())
+    {
+        width_for_strength = currTU.blocks[COMPONENT_Y].width;
+        height_for_strength = currTU.blocks[COMPONENT_Y].height;
+    }
+
+    const char* LUTrowPtr = getFilterLutParameters_chroma( std::min( uiWidth, uiHeight ), currTU.cu->predMode, qp + currTU.cs->pps->getCBIFQPOffset(), bfac, width_for_strength, height_for_strength, currTU.blocks[COMPONENT_Y].valid());
+
+    uint32_t   uiWidthExt = uiWidth + (NUMBER_PADDED_SAMPLES << 1);
+    uint32_t   uiHeightExt = uiHeight + (NUMBER_PADDED_SAMPLES << 1);
+
+    int iWidthExtSIMD = uiWidthExt;
+    if( uiWidth < 8 )
+    {
+        iWidthExtSIMD = 8 + (NUMBER_PADDED_SAMPLES << 1);
+    }
+
+    Pel *tempBlockPtr;
+
+    memset(tempblock, 0, iWidthExtSIMD*uiHeightExt * sizeof(short));
+
+    tempBlockPtr = tempblock + (NUMBER_PADDED_SAMPLES)* iWidthExtSIMD + NUMBER_PADDED_SAMPLES;
+
+    // Move block to temporary block
+    for (uint32_t uiY = 0; uiY < uiHeight; ++uiY)
+    {
+        std::memcpy(tempBlockPtr, srcPtr, uiWidth * sizeof(Pel));
+        srcPtr += srcStride;
+        tempBlockPtr += iWidthExtSIMD;
+    }
+    srcPtr = srcPtrTemp;
+    const CompArea &myArea = isCb ? currTU.blocks[COMPONENT_Cb] : currTU.blocks[COMPONENT_Cr];
+    bool topAltAvailable = myArea.y - 2 >= 0;
+    bool leftAltAvailable = myArea.x - 2 >= 0;
+    
+//    uint32_t chroma_pic_width = currTU.cu->slice->getSPS()->getMaxPicWidthInLumaSamples() >> 1;
+//    uint32_t chroma_pic_height = currTU.cu->slice->getSPS()->getMaxPicHeightInLumaSamples() >> 1;
+    int scaleX  = getComponentScaleX(compID, currTU.cu->cs->pcv->chrFormat);
+    int scaleY  = getComponentScaleY(compID, currTU.cu->cs->pcv->chrFormat);
+    uint32_t chroma_pic_width = currTU.cu->slice->getPPS()->getPicWidthInLumaSamples() >> scaleX;
+    uint32_t chroma_pic_height = currTU.cu->slice->getPPS()->getPicHeightInLumaSamples() >> scaleY;
+    
+    bool bottomAltAvailable = myArea.y + myArea.height + 1 < chroma_pic_height;
+    bool rightAltAvailable = myArea.x + myArea.width + 1 < chroma_pic_width;
+
+    bool allAvail = topAltAvailable && bottomAltAvailable && leftAltAvailable && rightAltAvailable;
+
+    //if not 420, then don't use rec for padding
+    if(currTU.cu->chromaFormat != CHROMA_420){
+        topAltAvailable = false;
+        bottomAltAvailable = false;
+        leftAltAvailable = false;
+        rightAltAvailable = false;
+        allAvail = false;
+    }
+    if(allAvail)
+    {
+        // set pointer two rows up and two pixels to the left from the start of the block
+        tempBlockPtr = tempblock;
+        // same with image data
+        srcPtr = srcPtr - 2*srcStride - 2;
+        // Move block to temporary block
+        // Check if the block a the top block of a CTU.
+        uint32_t CTUsize_chroma = currTU.cs->slice->getSPS()->getCTUSize() >> 1;
+        bool isCTUboundary = myArea.y % CTUsize_chroma == 0;
+
+        if(isCTUboundary)
+        {
+            // The samples two lines up are out of bounds. (One line above the CTU is OK, since SAO uses that line.)
+            // Hence the top line of tempblock is unavailable if the block is the top block of a CTU.
+            // Therefore, copy samples from one line up instead of from two lines up by updating srcPtr *before* copy.
+            srcPtr += srcStride;
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+        }
+        else
+        {
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+            srcPtr += srcStride;
+        }
+        tempBlockPtr += iWidthExtSIMD;
+        // Copy samples that are not out of bounds.
+        for (uint32_t uiY = 1; uiY < uiHeightExt-1; ++uiY)
+        {
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+            srcPtr += srcStride;
+            tempBlockPtr += iWidthExtSIMD;
+        }
+        // Check if the block is a bottom block of a CTU.
+        isCTUboundary = (myArea.y + uiHeight) % CTUsize_chroma == 0;
+        if(isCTUboundary)
+        {
+            // The samples two lines down are out of bounds. (One line below the CTU is OK, since SAO uses that line.)
+            // Hence the bottom line of tempblock is unavailable if the block at the bottom of a CTU.
+            // Therefore, copy samples from the second to last line instead of the last line by subtracting srcPtr before copy.
+            srcPtr -= srcStride;
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+        }
+        else
+        {
+            std::memcpy(tempBlockPtr, srcPtr, (uiWidthExt) * sizeof(Pel));
+        }
+    }
+    else
+    {
+        tempBlockPtr = tempblock + (NUMBER_PADDED_SAMPLES)* iWidthExtSIMD + NUMBER_PADDED_SAMPLES;
+        // Move block to temporary block
+        for (uint32_t uiY = 0; uiY < uiHeight; ++uiY)
+        {
+            std::memcpy(tempBlockPtr, srcPtr, uiWidth * sizeof(Pel));
+            srcPtr += srcStride;
+            tempBlockPtr += iWidthExtSIMD;
+        }
+        srcPtr = srcPtrTemp;
+        if(topAltAvailable)
+        {
+            std::copy(srcPtr - 2*srcStride, srcPtr - 2*srcStride + uiWidth, tempblock + 2);
+            std::copy(srcPtr - srcStride, srcPtr - srcStride + uiWidth, tempblock + iWidthExtSIMD + 2);
+        }
+        if(bottomAltAvailable)
+        {
+            std::copy(srcPtr + (uiHeight+1)*srcStride, srcPtr +(uiHeight+1)*srcStride + uiWidth, tempblock + (uiHeightExt-1)*iWidthExtSIMD + 2);
+            std::copy(srcPtr + uiHeight*srcStride, srcPtr +uiHeight*srcStride + uiWidth, tempblock + (uiHeightExt-2)*iWidthExtSIMD + 2);
+        }
+        if(leftAltAvailable)
+        {
+            for(int yy = 0; yy<uiHeight; yy++)
+            {
+                tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 0] = *(srcPtr + yy*srcStride -2);
+                tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 1] = *(srcPtr + yy*srcStride -1);
+            }
+        }
+        if(rightAltAvailable)
+        {
+            for(int yy = 0; yy<uiHeight; yy++)
+            {
+                tempblock[(iWidthExtSIMD<<1) + uiWidthExt-1 + yy*iWidthExtSIMD] = *(srcPtr + uiWidth + yy*srcStride + 1);
+                tempblock[(iWidthExtSIMD<<1) + uiWidthExt-2 + yy*iWidthExtSIMD] = *(srcPtr + uiWidth + yy*srcStride);
+            }
+        }
+        // if not all available, copy from inside tempbuffer
+        if(!topAltAvailable)
+        {
+            std::copy(tempblock + iWidthExtSIMD*2 + 2, tempblock + iWidthExtSIMD*2 + 2 + uiWidth, tempblock + 2);
+            std::copy(tempblock + iWidthExtSIMD*2 + 2, tempblock + iWidthExtSIMD*2 + 2 + uiWidth, tempblock + iWidthExtSIMD + 2);
+        }
+        if(!bottomAltAvailable)
+        {
+            std::copy(tempblock + (uiHeightExt-3)*iWidthExtSIMD + 2, tempblock + (uiHeightExt-3)*iWidthExtSIMD + 2 + uiWidth, tempblock + (uiHeightExt-2)*iWidthExtSIMD + 2);
+            std::copy(tempblock + (uiHeightExt-3)*iWidthExtSIMD + 2, tempblock + (uiHeightExt-3)*iWidthExtSIMD + 2 + uiWidth, tempblock + (uiHeightExt-1)*iWidthExtSIMD + 2);
+        }
+        if(!leftAltAvailable)
+        {
+            for(int yy = 0; yy<uiHeight; yy++)
+            {
+                tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 0] = tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 2];
+                tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 1] = tempblock[(iWidthExtSIMD<<1) + yy*iWidthExtSIMD + 2];
+            }
+        }
+        if(!rightAltAvailable)
+        {
+            for(int yy = 0; yy<uiHeight; yy++)
+            {
+                tempblock[(iWidthExtSIMD<<1) + uiWidthExt-2 + yy*iWidthExtSIMD] = tempblock[(iWidthExtSIMD<<1) + uiWidthExt-2 + yy*iWidthExtSIMD - 1];
+                tempblock[(iWidthExtSIMD<<1) + uiWidthExt-1 + yy*iWidthExtSIMD] = tempblock[(iWidthExtSIMD<<1) + uiWidthExt-2 + yy*iWidthExtSIMD - 1];
+            }
+        }
+        // All sides are available, easy to just copy corners also.
+        if(topAltAvailable && leftAltAvailable)
+        {
+            tempblock[0] = *(srcPtr - 2*srcStride -2);                                                // a     top left corner
+            tempblock[1] = *(srcPtr - 2*srcStride -1);                                                // b     a b|x x
+            tempblock[iWidthExtSIMD + 0] = *(srcPtr - srcStride -2);                                  // c     c d|x x
+            tempblock[iWidthExtSIMD + 1] = *(srcPtr - srcStride -1);                                  // d     -------
+        }
+        else
+        {
+            tempblock[0] = tempblock[iWidthExtSIMD*2 + 2];                                            // extend top left
+            tempblock[1] = tempblock[iWidthExtSIMD*2 + 2];                                            // extend top left
+            tempblock[iWidthExtSIMD + 0] = tempblock[iWidthExtSIMD*2 + 2];                            // extend top left
+            tempblock[iWidthExtSIMD + 1] = tempblock[iWidthExtSIMD*2 + 2];                            // extend top left
+        }
+        if(topAltAvailable && rightAltAvailable)
+        {
+            tempblock[iWidthExtSIMD - 2] = *(srcPtr - 2*srcStride + uiWidth);                         // a
+            tempblock[iWidthExtSIMD - 1] = *(srcPtr - 2*srcStride + uiWidth + 1);                     // b
+            tempblock[iWidthExtSIMD + uiWidthExt - 2] = *(srcPtr - srcStride + uiWidth);              // c
+            tempblock[iWidthExtSIMD + uiWidthExt - 1] = *(srcPtr - srcStride + uiWidth + 1);          // d
+        }
+        else
+        {
+            tempblock[iWidthExtSIMD - 2] = tempblock[iWidthExtSIMD*2 + uiWidthExt - 3];               // extend top right
+            tempblock[iWidthExtSIMD - 1] = tempblock[iWidthExtSIMD*2 + uiWidthExt - 3];               // extend top right
+            tempblock[iWidthExtSIMD + uiWidthExt - 2] = tempblock[iWidthExtSIMD*2 + uiWidthExt - 3];  // extend top right
+            tempblock[iWidthExtSIMD + uiWidthExt - 1] = tempblock[iWidthExtSIMD*2 + uiWidthExt - 3];  // extend top right
+        }
+        if(bottomAltAvailable && leftAltAvailable)
+        {
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + 0] = *(srcPtr + uiHeight*srcStride -2);          // a
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + 1] = *(srcPtr + uiHeight*srcStride -1);          // b
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + 0] = *(srcPtr + (uiHeight+1)*srcStride -2);      // c
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + 1] = *(srcPtr + (uiHeight+1)*srcStride -1);      // d
+        }
+        else
+        {
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + 0] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + 2];  // bot avail: mirror left/right
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + 1] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + 2];  // bot avail: mirror left/right
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + 0] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + 2];  // bot avail: mirror left/right
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + 1] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + 2];  // bot avail: mirror left/right
+        }
+        if(bottomAltAvailable && rightAltAvailable)
+        {
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt - 2] = *(srcPtr + uiHeight*srcStride + uiWidth);                // a
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt - 1] = *(srcPtr + uiHeight*srcStride + uiWidth + 1);            // b
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + uiWidthExt - 2] = *(srcPtr + (uiHeight+1)*srcStride + uiWidth);            // c
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + uiWidthExt - 1] = *(srcPtr + (uiHeight+1)*srcStride + uiWidth + 1);        // d
+        }
+        else
+        {
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt - 2] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + uiWidthExt - 3];
+            tempblock[iWidthExtSIMD*(uiHeightExt-2) + uiWidthExt - 1] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + uiWidthExt - 3];
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + uiWidthExt - 2] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + uiWidthExt - 3];
+            tempblock[iWidthExtSIMD*(uiHeightExt-1) + uiWidthExt - 1] = tempblock[iWidthExtSIMD*(uiHeightExt-3) + uiWidthExt - 3];
+        }
+    }
+
+    m_bilateralFilterDiamond5x5(uiWidth, uiHeight, tempblock, tempblockFiltered, clpRng, recPtr, recStride, iWidthExtSIMD, bfac, bif_round_add, bif_round_shift, false, LUTrowPtr );
+}
+
+void BilateralFilter::clipNotBilaterallyFilteredBlocks_chroma(const CPelUnitBuf& src, PelUnitBuf& rec, const ClpRng& clpRng, TransformUnit & currTU, bool isCb)
+{
+    PelUnitBuf myRecBuf = currTU.cs->getRecoBuf(currTU);
+    int cid = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+    if(myRecBuf.bufs[cid].width > 1)
+    {
+        // new result = old result (which is SAO-treated already) + diff due to bilateral filtering
+        myRecBuf.bufs[cid].copyClip(myRecBuf.bufs[cid], clpRng);
+    }
+    else
+    {
+        ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+        CompArea &compArea = currTU.block(compID);
+        const unsigned uiHeight = compArea.height;
+        int recStride = rec.get(compID).stride;
+        Pel *recPtr = rec.get(compID).bufAt(compArea);
+
+        for(uint32_t yy = 0; yy < uiHeight; yy++){
+            // new result = old result (which is SAO-treated already) + diff due to bilateral filtering
+            recPtr[0] = ClipPel<int>(recPtr[0], clpRng);
+            recPtr += recStride;
+        }
+    }
+}
+
+const char* BilateralFilter::getFilterLutParameters_chroma( const int size, const PredMode predMode, const int32_t qp, int& bfac, int width_for_strength, int height_for_strength, bool isLumaValid)
+{
+
+    int condition = std::min(width_for_strength, height_for_strength);
+
+    int T1 = 4;
+    int T2 = 16;
+    if(!isLumaValid)
+    {
+        T1 = 128;
+        T2 = 256;
+    }
+
+    if(predMode == MODE_INTER)
+    {
+        if(condition <= T1)
+        {
+            bfac = 2;
+            goto FINISH;
+        }
+        if (condition >= T2)
+        {
+            bfac = 1;
+            goto FINISH;
+        }
+        if(condition > T1 && condition < T2)
+        {
+            bfac = 2;
+            goto FINISH;
+        }
+    }
+    else{
+        if(condition <= T1)
+        {
+            bfac = 3;
+            goto FINISH;
+        }
+        if(condition >= T2)
+        {
+            bfac = 1;
+            goto FINISH;
+        }
+        if(condition > T1 && condition < T2)
+        {
+            bfac = 2;
+            goto  FINISH;
+        }
+    }
+
+FINISH:
+    int sqp = qp;
+    int qp_min = 17;
+    int qp_max = 42;
+    if( sqp < qp_min )
+    {
+        sqp = qp_min;
+    }
+    if( sqp > qp_max )
+    {
+        sqp = qp_max;
+    }
+
+    return m_wBIF_chroma[sqp - 17];
+
+}
+#endif
 
 #endif
diff --git a/source/Lib/CommonLib/BilateralFilter.h b/source/Lib/CommonLib/BilateralFilter.h
index a77d6d62e9d085d5a39aa5514e1dbcad69d358c4..66b4ceb29fdabb20b8fb03322a45765c40263a60 100755
--- a/source/Lib/CommonLib/BilateralFilter.h
+++ b/source/Lib/CommonLib/BilateralFilter.h
@@ -35,21 +35,30 @@
 
 #include "CommonDef.h"
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 
 #include "Unit.h"
 #include "Buffer.h"
 #include <tmmintrin.h>
 #include <smmintrin.h>
 #include <immintrin.h>
-
+#if JVET_V0094_BILATERAL_FILTER
 class BIFCabacEst
 {
 public:
   virtual ~BIFCabacEst() {};
   virtual uint64_t getBits(const Slice& slice, const BifParams& htdfParams) = 0;
 };
-
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+class CBIFCabacEst
+{
+public:
+    virtual ~CBIFCabacEst() {};
+    virtual uint64_t getBits_Cb(const Slice& slice, const CBifParams& htdfParams) = 0;
+    virtual uint64_t getBits_Cr(const Slice& slice, const CBifParams& htdfParams) = 0;
+};
+#endif
 class BilateralFilter
 {
 private:
@@ -72,7 +81,7 @@ private:
 #if JVET_W0066_CCSAO
   static void blockBilateralFilterDiamond5x5NoClip(uint32_t uiWidth, uint32_t uiHeight, int16_t block[], int16_t blkFilt[], const ClpRng& clpRng, Pel* recPtr, int recStride, int iWidthExtSIMD, int bfac, int bif_round_add, int bif_round_shift, bool isRDO, const char* LUTrowPtr);
 #endif
-  
+#if JVET_V0094_BILATERAL_FILTER
   char m_wBIF[26][16] = {
   {  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
   {  0,   1,   1,   1,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
@@ -101,14 +110,44 @@ private:
   {  0,  16,  23,  32,  36,  40,  43,  43,  44,  42,  39,  26,  21,  17,  15,  -3, },
   {  0,  17,  23,  33,  37,  41,  44,  44,  45,  44,  42,  27,  22,  17,  15,  -3, },
   };
-  
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  char m_wBIF_chroma[26][16] = {
+  {   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+  {   0,   1,   1,   1,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+  {   0,   2,   2,   2,   1,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0, },
+  {   0,   2,   2,   2,   2,   1,   1,   1,   1,   1,   1,   1,   0,   1,   1,   -1, },
+  {   0,   2,   2,   2,   2,   2,   1,   2,   1,   1,   1,   1,   0,   1,   1,   -1, },
+  {   0,   3,   3,   3,   2,   2,   1,   2,   1,   1,   1,   1,   0,   1,   1,   -1, },
+  {   0,   4,   4,   4,   3,   2,   2,   2,   2,   2,   2,   1,   0,   1,   1,   -1, },
+  {   0,   5,   5,   5,   4,   2,   2,   2,   2,   2,   2,   1,   1,   1,   1,   -1, },
+  {   0,   5,   6,   6,   4,   3,   2,   2,   2,   2,   2,   2,   1,   2,   2,   -2, },
+  {   0,   5,   8,   8,   5,   3,   3,   3,   3,   2,   2,   2,   2,   2,   2,   -2, },
+  {   0,   6,   9,   9,   5,   4,   4,   3,   4,   3,   3,   2,   2,   2,   2,   -2, },
+  {   0,   6,   9,  10,   8,   6,   6,   5,   5,   5,   4,   2,   2,   2,   2,   -2, },
+  {   0,   6,  10,  11,  10,   9,   9,   6,   6,   5,   5,   4,   4,   3,   3,   -2, },
+  {   0,   7,  11,  12,  12,  12,  11,   9,   7,   7,   6,   5,   5,   4,   5,   -2, },
+  {   0,   7,  12,  13,  15,  15,  13,  10,   9,   8,   8,   6,   6,   5,   5,   -2, },
+  {   0,   7,  12,  15,  17,  17,  16,  12,   9,   9,   9,   7,   7,   5,   6,   -2, },
+  {   0,   8,  13,  16,  19,  20,  19,  16,  14,  13,  12,   9,   9,   7,   7,   -2, },
+  {   0,   8,  14,  18,  20,  22,  22,  20,  18,  17,  14,  11,  10,   9,   9,   -2, },
+  {   0,   9,  15,  19,  23,  23,  25,  23,  23,  20,  17,  13,  12,  10,   9,   -2, },
+  {   0,   9,  16,  20,  24,  26,  28,  27,  27,  24,  20,  15,  13,  12,  11,   -2, },
+  {   0,   9,  16,  22,  26,  28,  31,  31,  31,  28,  23,  17,  15,  13,  12,   -2, },
+  {   0,  10,  16,  23,  27,  29,  32,  32,  32,  30,  25,  18,  16,  13,  12,   -2, },
+  {   0,  11,  17,  23,  27,  30,  33,  33,  33,  30,  27,  19,  16,  13,  12,   -2, },
+  {   0,  12,  17,  24,  27,  30,  33,  33,  34,  32,  29,  20,  16,  13,  12,   -2, },
+  {   0,  12,  18,  25,  28,  31,  34,  34,  34,  33,  30,  20,  16,  13,  12,   -2, },
+  {   0,  13,  18,  26,  29,  32,  34,  34,  35,  34,  33,  21,  17,  13,  12,   -2, },
+  };
+#endif
 public:
   BilateralFilter();
   ~BilateralFilter();
 
   void create();
   void destroy();
-
+#if JVET_V0094_BILATERAL_FILTER
   void bilateralFilterRDOdiamond5x5(PelBuf& resiBuf, const CPelBuf& predBuf, PelBuf& recoBuf, int32_t qp, const CPelBuf& recIPredBuf, const ClpRng& clpRng, TransformUnit & currTU, bool useReco, bool doReshape, std::vector<Pel>& pLUT);
   
   void bilateralFilterPicRDOperCTU(CodingStructure& cs, PelUnitBuf& src,BIFCabacEst* BifCABACEstimator);
@@ -116,12 +155,29 @@ public:
   void bilateralFilterDiamond5x5(const CPelUnitBuf& src, PelUnitBuf& rec, int32_t qp, const ClpRng& clpRng, TransformUnit & currTU);
 #if JVET_W0066_CCSAO
   void bilateralFilterDiamond5x5NoClip(const CPelUnitBuf& src, PelUnitBuf& rec, int32_t qp, const ClpRng& clpRng, TransformUnit& currTU);
+#endif
 #endif
   void clipNotBilaterallyFilteredBlocks(const CPelUnitBuf& src, PelUnitBuf& rec, const ClpRng& clpRng, TransformUnit & currTU);
-
+#if JVET_V0094_BILATERAL_FILTER
   const char* getFilterLutParameters( const int size, const PredMode predMode, const int qp, int& bfac );
+#endif
+
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  void bilateralFilterRDOdiamond5x5_chroma(PelBuf& resiBuf, const CPelBuf& predBuf, PelBuf& recoBuf, int32_t qp, const CPelBuf& recIPredBuf, const ClpRng& clpRng, TransformUnit & currTU, bool useReco, bool isCb);
+
+  void bilateralFilterPicRDOperCTU_chroma(CodingStructure& cs, PelUnitBuf& src, CBIFCabacEst* CBifCABACEstimator, bool isCb);
+
+  void bilateralFilterDiamond5x5_chroma(const CPelUnitBuf& src, PelUnitBuf& rec, int32_t qp, const ClpRng& clpRng, TransformUnit & currTU, bool isCb);
+
+  void clipNotBilaterallyFilteredBlocks_chroma(const CPelUnitBuf& src, PelUnitBuf& rec, const ClpRng& clpRng, TransformUnit & currTU, bool isCb);
+
+#if JVET_W0066_CCSAO
+    void bilateralFilterDiamond5x5NoClip_chroma(const CPelUnitBuf& src, PelUnitBuf& rec, int32_t qp, const ClpRng& clpRng, TransformUnit & currTU, bool isCb);
+#endif
+    const char* getFilterLutParameters_chroma( const int size, const PredMode predMode, const int qp, int& bfac, int width_for_strength, int height_for_strength, bool isLumaValid);
+#endif
 
-#if ENABLE_SIMD_BILATERAL_FILTER
+#if ENABLE_SIMD_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER_ENABLE_SIMD
 #ifdef TARGET_SIMD_X86
   template<X86_VEXT vext>
   static void simdFilterDiamond5x5( uint32_t uiWidth, uint32_t uiHeight, int16_t block[], int16_t blkFilt[], const ClpRng& clpRng, Pel* recPtr, int recStride, int iWidthExtSIMD, int bfac, int bif_round_add, int bif_round_shift, bool isRDO, const char* LUTrowPtr );
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 351a3f1bd92886805d98e45e8ae63b179c1159c9..ccad654c5f62b56c53eb64c8a2a011d280f64e35 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -124,7 +124,7 @@ static const int AFFINE_ME_LIST_SIZE =                             4;
 static const int AFFINE_ME_LIST_SIZE_LD =                          3;
 static const double AFFINE_ME_LIST_MVP_TH =                        1.0;
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 static const int32_t NUMBER_PADDED_SAMPLES =  2;
 static const int32_t BIF_ROUND_ADD =         32;
 static const int32_t BIF_ROUND_SHIFT =        6;
@@ -237,8 +237,13 @@ static const int MAX_ALF_FILTER_LENGTH       =                      7;
 #endif
 static const int MAX_NUM_ALF_COEFF           =                     MAX_ALF_FILTER_LENGTH * MAX_ALF_FILTER_LENGTH / 2 + 1;
 static const int MAX_ALF_PADDING_SIZE        =                      4;
+#if JVET_X0071_LONGER_CCALF
+#define MAX_NUM_CC_ALF_FILTERS                                      16
+static constexpr int MAX_NUM_CC_ALF_CHROMA_COEFF    =               25;
+#else
 #define MAX_NUM_CC_ALF_FILTERS                                      4
 static constexpr int MAX_NUM_CC_ALF_CHROMA_COEFF    =               8;
+#endif
 static constexpr int CCALF_DYNAMIC_RANGE            =               6;
 static constexpr int CCALF_BITS_PER_COEFF_LEVEL     =               3;
 
@@ -247,6 +252,11 @@ static const int ALF_CTB_MAX_NUM_APS         =                      8;
 #if ALF_IMPROVEMENT
 static const int ALF_ORDER                   =                      4;
 static const int NUM_FIXED_FILTER_SETS       =                      2;
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+static const int ALF_NUM_CLASSIFIER          =                      2;
+static const int ALF_CLASSES_NEW             =                     25;
+static const int ALF_NUM_CLASSES_CLASSIFIER[ALF_NUM_CLASSIFIER] = { MAX_NUM_ALF_CLASSES,  ALF_CLASSES_NEW };
+#endif
 #else 
 static const int NUM_FIXED_FILTER_SETS       =                     16;
 static const int NUM_TOTAL_FILTER_SETS       =                     NUM_FIXED_FILTER_SETS + ALF_CTB_MAX_NUM_APS;
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 74b50910a5f2dd494c13e20323ed27d1e29d6efe..2be41cd7ca5d8b2310e78bba8a2bf740626cf303 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -1526,6 +1526,26 @@ const CtxSet ContextSetCfg::BifCtrlFlags = ContextSetCfg::addCtxSet
   { DWS }
 });
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+const CtxSet ContextSetCfg::CBifCtrlFlags_Cb = ContextSetCfg::addCtxSet
+({
+  { 39 },
+  { 39 },
+  { 39 },
+  { DWS },
+  { DWS },
+  { DWS }
+});
+const CtxSet ContextSetCfg::CBifCtrlFlags_Cr = ContextSetCfg::addCtxSet
+({
+  { 39 },
+  { 39 },
+  { 39 },
+  { DWS },
+  { DWS },
+  { DWS }
+});
+#endif
 
 #if JVET_W0066_CCSAO
 const CtxSet ContextSetCfg::CcSaoControlIdc = ContextSetCfg::addCtxSet
@@ -2599,6 +2619,22 @@ const CtxSet ContextSetCfg::BifCtrlFlags = ContextSetCfg::addCtxSet
   { DWS, },
 });
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+const CtxSet ContextSetCfg::CBifCtrlFlags_Cb = ContextSetCfg::addCtxSet
+({
+    { 39, },
+    { 39, },
+    { 39, },
+    { DWS, },
+});
+const CtxSet ContextSetCfg::CBifCtrlFlags_Cr = ContextSetCfg::addCtxSet
+({
+    { 39, },
+    { 39, },
+    { 39, },
+    { DWS, },
+});
+#endif
 
 #if JVET_W0066_CCSAO
 const CtxSet ContextSetCfg::CcSaoControlIdc = ContextSetCfg::addCtxSet
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index 3841a64a00256170e7dc0619b12a0576ff544772..fcfc81ab9ffa15a9d28fb5f286b0f0dbe72b43e3 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -304,6 +304,10 @@ public:
 #if JVET_V0094_BILATERAL_FILTER
   static const CtxSet   BifCtrlFlags;
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  static const CtxSet   CBifCtrlFlags_Cb;
+  static const CtxSet   CBifCtrlFlags_Cr;
+#endif
 #if JVET_W0066_CCSAO
   static const CtxSet   CcSaoControlIdc;
 #endif
diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp
index af2e04a61c09ffed5087ff1dd899a606269233de..6c8b18113365597fe9b436e6f9f95623801ec236 100644
--- a/source/Lib/CommonLib/Picture.cpp
+++ b/source/Lib/CommonLib/Picture.cpp
@@ -42,6 +42,9 @@
 #if JVET_V0094_BILATERAL_FILTER
 BifParams Picture::m_BifParams;
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+CBifParams Picture::m_CBifParams;
+#endif
 
 #if ENABLE_SPLIT_PARALLELISM
 
diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h
index 26db42887758f7b8f95e24c711d809042803d534..1aa7a10a8b9ae34be81924080b630d522af7f377 100644
--- a/source/Lib/CommonLib/Picture.h
+++ b/source/Lib/CommonLib/Picture.h
@@ -314,7 +314,17 @@ public:
     std::fill(m_BifParams.ctuOn.begin(), m_BifParams.ctuOn.end(), 0);
   };
 #endif
-  
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  CBifParams&       getCBifParam() { return m_CBifParams; }
+  void resizeBIF_Chroma(unsigned numEntries)
+  {
+    m_CBifParams.numBlocks = numEntries;
+    m_CBifParams.ctuOn_Cb.resize(numEntries);
+    m_CBifParams.ctuOn_Cr.resize(numEntries);
+    std::fill(m_CBifParams.ctuOn_Cb.begin(), m_CBifParams.ctuOn_Cb.end(), 0);
+    std::fill(m_CBifParams.ctuOn_Cr.begin(), m_CBifParams.ctuOn_Cr.end(), 0);
+  }
+#endif
 #if ENABLE_QPA
   std::vector<double>     m_uEnerHpCtu;                         ///< CTU-wise L2 or squared L1 norm of high-passed luma input
   std::vector<Pel>        m_iOffsetCtu;                         ///< CTU-wise DC offset (later QP index offset) of luma input
@@ -327,7 +337,9 @@ public:
 #if JVET_V0094_BILATERAL_FILTER
   static BifParams        m_BifParams;
 #endif
-
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  static CBifParams       m_CBifParams;
+#endif
   std::vector<uint8_t> m_alfCtuEnableFlag[MAX_NUM_COMPONENT];
   uint8_t* getAlfCtuEnableFlag( int compIdx ) { return m_alfCtuEnableFlag[compIdx].data(); }
   std::vector<uint8_t>* getAlfCtuEnableFlag() { return m_alfCtuEnableFlag; }
diff --git a/source/Lib/CommonLib/SampleAdaptiveOffset.cpp b/source/Lib/CommonLib/SampleAdaptiveOffset.cpp
index 5ab4acb18f1b46a724ffcf7036e80ee757d3424e..1f7f3da409d2f37f19a987fa51bd743757274d59 100644
--- a/source/Lib/CommonLib/SampleAdaptiveOffset.cpp
+++ b/source/Lib/CommonLib/SampleAdaptiveOffset.cpp
@@ -595,7 +595,7 @@ void SampleAdaptiveOffset::offsetBlock(const int channelBitDepth, const ClpRng&
   }
 }
 
-#if JVET_V0094_BILATERAL_FILTER || JVET_W0066_CCSAO
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER || JVET_W0066_CCSAO
 void SampleAdaptiveOffset::offsetBlockNoClip(const int channelBitDepth, const ClpRng& clpRng, int typeIdx, int* offset
                                              , const Pel* srcBlk, Pel* resBlk, int srcStride, int resStride,  int width, int height
                                              , bool isLeftAvail,  bool isRightAvail, bool isAboveAvail, bool isBelowAvail, bool isAboveLeftAvail, bool isAboveRightAvail, bool isBelowLeftAvail, bool isBelowRightAvail)
@@ -884,7 +884,7 @@ void SampleAdaptiveOffset::offsetCTU( const UnitArea& area, const CPelUnitBuf& s
   } //compIdx
 }
 
-#if JVET_V0094_BILATERAL_FILTER || JVET_W0066_CCSAO
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER || JVET_W0066_CCSAO
 void SampleAdaptiveOffset::offsetCTUnoClip( const UnitArea& area, const CPelUnitBuf& src, PelUnitBuf& res, SAOBlkParam& saoblkParam, CodingStructure& cs)
 {
   const uint32_t numberOfComponents = getNumberValidComponents( area.chromaFormat );
@@ -943,7 +943,7 @@ void SampleAdaptiveOffset::offsetCTUnoClip( const UnitArea& area, const CPelUnit
         verVirBndryPosComp[i] = (verVirBndryPos[i] >> ::getComponentScaleX(compID, area.chromaFormat)) - compArea.x;
       }
 #endif
-#if JVET_W0066_CCSAO    
+#if JVET_W0066_CCSAO
       // Do not clip the final output for both luma and chroma. Clipping is done jontly for SAO/BIF/CCSAO.
       
 	    offsetBlockNoClip(cs.sps->getBitDepth(toChannelType(compID)),
@@ -956,8 +956,12 @@ void SampleAdaptiveOffset::offsetCTUnoClip( const UnitArea& area, const CPelUnit
 		    , isBelowLeftAvail, isBelowRightAvail
 		  //                 , isCtuCrossedByVirtualBoundaries, horVirBndryPosComp, verVirBndryPosComp, numHorVirBndry, numVerVirBndry
 	    );    
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if(isLuma(compID) || isChroma(compID))
 #else
       if(compID == COMPONENT_Y)
+#endif
       {
         // If it is luma we should not clip, since we will clip
         // after BIF has been added.
@@ -996,12 +1000,11 @@ void SampleAdaptiveOffset::offsetCTUnoClip( const UnitArea& area, const CPelUnit
 }
 #endif
 
-void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkParams
-                                      )
+void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkParams )
 {
   CHECK(!saoBlkParams, "No parameters present");
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   // In code without BIF, SAOProcess would not be run if 'SAO=0'.
   // However, in the BIF-enabled code, we still might go here if 'SAO=0' and 'BIF=1'.
   // Hence we must check getSAOEnabledFlag() for some of the function calls.
@@ -1015,7 +1018,7 @@ void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkP
 
   const uint32_t numberOfComponents = getNumberValidComponents(cs.area.chromaFormat);
   bool bAllDisabled = true;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   // If 'SAO=0' we would not normally get here. However, now we might get
   // here if 'SAO=0' and 'BIF=1'. Hence we should only run this if
   // getSAOEnabledFlag() is true. Note that if getSAOEnabledFlag() is false,
@@ -1031,21 +1034,32 @@ void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkP
       bAllDisabled = false;
     }
   }
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   }
 #endif
   if (bAllDisabled)
   {
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if(!cs.pps->getUseBIF() && !cs.pps->getUseCBIF())
+#else
     // Even if we are not doing SAO we might still need to do BIF
     // so we cannot return even if SAO is never used.
     if(!cs.pps->getUseBIF())
+#endif
     {
       // However, if we are not doing BIF it is safe to return.
       return;
     }
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if(!cs.pps->getUseCBIF())
+    {
+        return;
+    }
 #else
     return;
+#endif
 #endif
   }
 
@@ -1068,7 +1082,6 @@ void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkP
       {
         offsetCTUnoClip( area, m_tempBuf, rec, cs.picture->getSAO()[ctuRsAddr], cs );
       }
-
 #if JVET_V0094_BILATERAL_FILTER
       if (cs.pps->getUseBIF())
       {
@@ -1088,9 +1101,66 @@ void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkP
         }
       }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if(cs.pps->getUseCBIF())
+      {
+        bool TU_VALID = false;
+        bool TU_CBF = false;
+        bool isDualTree = CS::isDualITree(cs);
+        ChannelType CType = isDualTree ? CH_C : CH_L;
+        bool BIF_chroma = false;
+        CBifParams& CBifParams = cs.picture->getCBifParam();
+
+        // And now we traverse the CTU to do BIF
+        for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+        {
+            bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+            if(!chroma_valid)
+            {
+                continue;
+            }
+            for (auto &currTU : CU::traverseTUs(currCU))
+            {
+                bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+
+                for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                {
+                    BIF_chroma = false;
+                    bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                    ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+                    bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                    if(!isDualTree)
+                    {
+                        TU_VALID = currTU.blocks[compIdx].valid();
+                        TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+                        if(TU_VALID)
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                        }
+                        BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                    }
+                    else
+                    {
+                        TU_CBF = TU::getCbf(currTU, compID);
+                        BIF_chroma = (CTU_ON && (( TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                    }
+                    if(BIF_chroma)
+                    {
+                        m_bilateralFilter.bilateralFilterDiamond5x5NoClip_chroma(m_tempBuf, rec, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                    }
+                }
+            }
+        }
+    }
+#endif
 #else
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if(cs.pps->getUseBIF() || cs.pps->getUseCBIF())
+#else
       if(cs.pps->getUseBIF())
+#endif
       {
         // We are using BIF, so we run SAO without clipping
         // However, if 'SAO=0', bAllDisabled=true and we should not run offsetCTUnoClip.
@@ -1105,6 +1175,22 @@ void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkP
         bool clipLumaIfNoBilat = false;
         if(!bAllDisabled && myCtbOffset.modeIdc != SAO_MODE_OFF)
           clipLumaIfNoBilat = true;
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        SAOOffset& myCtbOffset_Cb     = mySAOblkParam[1];
+        SAOOffset& myCtbOffset_Cr     = mySAOblkParam[2];
+        CBifParams& CBifParams = cs.picture->getCBifParam();
+
+        bool clipChromaIfNoBilat_Cb = false;
+        bool clipChromaIfNoBilat_Cr = false;
+
+        if(!bAllDisabled && myCtbOffset_Cb.modeIdc != SAO_MODE_OFF)
+            clipChromaIfNoBilat_Cb = true;
+        if(!bAllDisabled && myCtbOffset_Cr.modeIdc != SAO_MODE_OFF)
+            clipChromaIfNoBilat_Cr = true;
+
+        if(cs.pps->getUseBIF())
+        {
+#endif
         
         // And now we traverse the CTU to do BIF
         for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
@@ -1125,15 +1211,235 @@ void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs, SAOBlkParam* saoBlkP
             }
           }
         }
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        } // BIF LUMA is disabled
+        else
+        {
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
+            {
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    if(clipLumaIfNoBilat)
+                    {
+                        m_bilateralFilter.clipNotBilaterallyFilteredBlocks(m_tempBuf, rec, cs.slice->clpRng(COMPONENT_Y), currTU);
+                    }
+                }
+            }
+        }
+        if(cs.pps->getUseCBIF())
+        {
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+            bool BIF_chroma = false;
+            // And now we traverse the CTU to do BIF
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+                    for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                    {
+                        bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                        ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+                        bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                        BIF_chroma = false;
+                        if(!isDualTree)
+                        {
+                            TU_VALID =  currTU.blocks[compIdx].valid();
+                            TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+                            if(TU_VALID)
+                            {
+                                TU_CBF =TU::getCbf(currTU, compID);
+                            }
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                        }
+                        else
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                        }
+
+                        if(BIF_chroma)
+                        {
+                            m_bilateralFilter.bilateralFilterDiamond5x5_chroma(m_tempBuf, rec, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                        }
+                        else
+                        {
+                            bool use_clip = isCb ? clipChromaIfNoBilat_Cb : clipChromaIfNoBilat_Cr;
+                            if(use_clip && currTU.blocks[compIdx].valid())
+                                m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(m_tempBuf, rec, cs.slice->clpRng(compID), currTU, isCb);
+                        }
+                    }
+                }
+            }
+        }// BIF chroma is disabled
+        else
+        {
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    if(clipChromaIfNoBilat_Cb && currTU.blocks[COMPONENT_Cb].valid())
+                    {
+                        m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(m_tempBuf, rec, cs.slice->clpRng(COMPONENT_Cb), currTU, true);
+                    }
+                    if(clipChromaIfNoBilat_Cr && currTU.blocks[COMPONENT_Cr].valid())
+                    {
+                        m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(m_tempBuf, rec, cs.slice->clpRng(COMPONENT_Cr), currTU, false);
+                    }
+                  }
+              }
+          }
+#endif
       }
       else
       {
         // BIF is not used, use old SAO code
         offsetCTU( area, m_tempBuf, rec, cs.picture->getSAO()[ctuRsAddr], cs);
       }
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if( !bAllDisabled )
+        {
+            offsetCTUnoClip( area, m_tempBuf, rec, cs.picture->getSAO()[ctuRsAddr], cs);
+        }
+
+        SAOBlkParam mySAOblkParam = cs.picture->getSAO()[ctuRsAddr];
+        SAOOffset& myCtbOffset     = mySAOblkParam[0];
+
+        bool clipLumaIfNoBilat = false;
+        if(!bAllDisabled && myCtbOffset.modeIdc != SAO_MODE_OFF)
+        {
+            clipLumaIfNoBilat = true;
+        }
+
+        SAOOffset& myCtbOffset_Cb     = mySAOblkParam[1];
+        SAOOffset& myCtbOffset_Cr     = mySAOblkParam[2];
+        CBifParams& CBifParams = cs.picture->getCBifParam();
+
+        bool clipChromaIfNoBilat_Cb = false;
+        bool clipChromaIfNoBilat_Cr = false;
+
+        if(!bAllDisabled && myCtbOffset_Cb.modeIdc != SAO_MODE_OFF)
+        {
+            clipChromaIfNoBilat_Cb = true;
+        }
+        if(!bAllDisabled && myCtbOffset_Cr.modeIdc != SAO_MODE_OFF)
+        {
+            clipChromaIfNoBilat_Cr = true;
+        }
+
+        for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
+        {
+            for (auto &currTU : CU::traverseTUs(currCU))
+            {
+                if(clipLumaIfNoBilat)
+                {
+                    m_bilateralFilter.clipNotBilaterallyFilteredBlocks(m_tempBuf, rec, cs.slice->clpRng(COMPONENT_Y), currTU);
+                }
+            }
+        }
+
+        if(cs.pps->getUseCBIF())
+        {
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+            bool BIF_chroma = false;
+            // And now we traverse the CTU to do BIF
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+                    for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                    {
+                        bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                        ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+                        bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                        BIF_chroma = false;
+                        if(!isDualTree)
+                        {
+                            TU_VALID = currTU.blocks[compIdx].valid();
+                            TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+                            if(TU_VALID)
+                            {
+                                TU_CBF = TU::getCbf(currTU, compID);
+                            }
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                        }
+                        else
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                        }
+                        if(BIF_chroma)
+                        {
+                            m_bilateralFilter.bilateralFilterDiamond5x5_chroma(m_tempBuf, rec, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                        }
+                        else
+                        {
+                            bool use_clip = isCb ? clipChromaIfNoBilat_Cb : clipChromaIfNoBilat_Cr;
+                            if(use_clip && currTU.blocks[compIdx].valid())
+                            {
+                                m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(m_tempBuf, rec, cs.slice->clpRng(compID), currTU, true);
+                            }
+                        }
+                    }
+                }
+            }
+        }// BIF chroma off
+        else
+        {
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    if(clipChromaIfNoBilat_Cb && currTU.blocks[COMPONENT_Cb].valid())
+                    {
+                        m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(m_tempBuf, rec, cs.slice->clpRng(COMPONENT_Cb), currTU, true);
+                    }
+                    if(clipChromaIfNoBilat_Cr && currTU.blocks[COMPONENT_Cr].valid())
+                    {
+                        m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(m_tempBuf, rec, cs.slice->clpRng(COMPONENT_Cr), currTU, false);
+                    }
+                }
+            }
+        }
 #else
       offsetCTU( area, m_tempBuf, rec, cs.picture->getSAO()[ctuRsAddr], cs);
 #endif
+#endif
 #endif
       ctuRsAddr++;
     }
@@ -1194,16 +1500,21 @@ void SampleAdaptiveOffset::applyCcSao(CodingStructure &cs, const PreCalcValues&
 void SampleAdaptiveOffset::jointClipSaoBifCcSao(CodingStructure& cs)
 {
 #if JVET_V0094_BILATERAL_FILTER
-  if( !cs.sps->getSAOEnabledFlag() && !cs.pps->getUseBIF() && !cs.sps->getCCSAOEnabledFlag() )
-  {
-    return;
-  }
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if (!cs.sps->getSAOEnabledFlag() && !cs.pps->getUseBIF() && !cs.pps->getUseCBIF() && !cs.sps->getCCSAOEnabledFlag())
+#else
+  if (!cs.sps->getSAOEnabledFlag() && !cs.pps->getUseBIF() && !cs.sps->getCCSAOEnabledFlag())
+#endif
 #else
-  if( !cs.sps->getSAOEnabledFlag() && !cs.sps->getCCSAOEnabledFlag() )
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if (!cs.sps->getSAOEnabledFlag() && !cs.sps->getCCSAOEnabledFlag() && !cs.pps->getUseCBIF())
+#else
+  if (!cs.sps->getSAOEnabledFlag() && !cs.sps->getCCSAOEnabledFlag())
+#endif
+#endif
   {
     return;
   }
-#endif
 
   const PreCalcValues& pcv = *cs.pcv;
   PelUnitBuf dstYuv = cs.getRecoBuf();
@@ -1263,6 +1574,60 @@ void SampleAdaptiveOffset::jointClipSaoBifCcSao(CodingStructure& cs)
               }
             }
           }
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+          if(cs.pps->getUseCBIF())
+          {
+            if(compIdx == COMPONENT_Cb || compIdx == COMPONENT_Cr)
+            {
+                CBifParams& CBifParams = cs.picture->getCBifParam();
+                bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+
+                bool TU_VALID = false;
+                bool TU_CBF = false;
+                bool CTU_ON = false;
+                bool isDualTree = CS::isDualITree(cs);
+                ChannelType CType = isDualTree ? CH_C : CH_L;
+                bool BIF_chroma = false;
+                for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+                {
+                    bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                    if(!chroma_valid)
+                    {
+                        continue;
+                    }
+
+                    for (auto &currTU : CU::traverseTUs(currCU))
+                    {
+                        bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+                        //Cb or Cr
+                        BIF_chroma = false;
+                        CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                        if(!isDualTree)
+                        {
+                            TU_VALID = currTU.blocks[compIdx].valid();
+                            TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+                            if(TU_VALID)
+                            {
+                                TU_CBF = TU::getCbf(currTU, compID);
+                            }
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                        }
+                        else
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                        }
+                        if(BIF_chroma)
+                        {
+                            m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(m_tempBuf, dstYuv, cs.slice->clpRng(compID) , currTU, isCb);
+                        }
+                    }
+                }
+            }
+          }
+#endif
         }
 #endif
       }
diff --git a/source/Lib/CommonLib/SampleAdaptiveOffset.h b/source/Lib/CommonLib/SampleAdaptiveOffset.h
index ff661085a99e94af76ede7543540a3632fb22922..d5ed6c1827c56c5ae2c77918be2d54579f5b87ef 100644
--- a/source/Lib/CommonLib/SampleAdaptiveOffset.h
+++ b/source/Lib/CommonLib/SampleAdaptiveOffset.h
@@ -41,7 +41,7 @@
 #include "CommonDef.h"
 #include "Unit.h"
 #include "Reshape.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "BilateralFilter.h"
 #endif
 //! \ingroup CommonLib
@@ -74,7 +74,7 @@ public:
   void destroy();
   static int getMaxOffsetQVal(const int channelBitDepth) { return (1<<(std::min<int>(channelBitDepth,MAX_SAO_TRUNCATED_BITDEPTH)-5))-1; } //Table 9-32, inclusive
   void setReshaper(Reshape * p) { m_pcReshape = p; }
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter m_bilateralFilter;
 #endif
 #if RPR_ENABLE
@@ -103,7 +103,9 @@ protected:
                   , bool isLeftAvail, bool isRightAvail, bool isAboveAvail, bool isBelowAvail, bool isAboveLeftAvail, bool isAboveRightAvail, bool isBelowLeftAvail, bool isBelowRightAvail
                   , bool isCtuCrossedByVirtualBoundaries, int horVirBndryPos[], int verVirBndryPos[], int numHorVirBndry, int numVerVirBndry
     );
-#if JVET_V0094_BILATERAL_FILTER || JVET_W0066_CCSAO
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER || JVET_W0066_CCSAO
+  void offsetBlockBIFonly(const int channelBitDepth, const ClpRng& clpRng, int typeIdx, int* offset, const Pel* srcBlk, Pel* resBlk, int srcStride, int resStride,  int width, int height
+                          , bool isLeftAvail, bool isRightAvail, bool isAboveAvail, bool isBelowAvail, bool isAboveLeftAvail, bool isAboveRightAvail, bool isBelowLeftAvail, bool isBelowRightAvail, CodingStructure &cs, const UnitArea &area);
   void offsetBlockNoClip(const int channelBitDepth, const ClpRng& clpRng, int typeIdx, int* offset
                          , const Pel* srcBlk, Pel* resBlk, int srcStride, int resStride,  int width, int height
                          , bool isLeftAvail,  bool isRightAvail, bool isAboveAvail, bool isBelowAvail, bool isAboveLeftAvail, bool isAboveRightAvail, bool isBelowLeftAvail, bool isBelowRightAvail);
@@ -112,7 +114,7 @@ protected:
   void reconstructBlkSAOParam(SAOBlkParam& recParam, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES]);
   int  getMergeList(CodingStructure& cs, int ctuRsAddr, SAOBlkParam* blkParams, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES]);
   void offsetCTU(const UnitArea& area, const CPelUnitBuf& src, PelUnitBuf& res, SAOBlkParam& saoblkParam, CodingStructure& cs);
-#if JVET_V0094_BILATERAL_FILTER || JVET_W0066_CCSAO
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER || JVET_W0066_CCSAO
   void offsetCTUnoClip( const UnitArea& area, const CPelUnitBuf& src, PelUnitBuf& res, SAOBlkParam& saoblkParam, CodingStructure& cs);
 #endif
 #if JVET_W0066_CCSAO
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 9b0a36b9af18b9fefdb7c8ff663caea96b603b41..00892e59e3e83d4860c38db5ce977ba2966950a3 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -3396,6 +3396,11 @@ PPS::PPS()
 , m_BIFStrength                      (1)
 , m_BIFQPOffset                      (0)
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+, m_CBIF                              (false)
+, m_CBIFStrength                      (1)
+, m_CBIFQPOffset                      (0)
+#endif
 , pcv                                (NULL)
 {
   m_ChromaQpAdjTableIncludingNullEntry[0].u.comp.CbOffset = 0; // Array includes entry [0] for the null offset used when cu_chroma_qp_offset_flag=0. This is initialised here and never subsequently changed.
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index b55d01a23d0c222acbdf3b2caaedbf07cf9b6575..2e0be241533702cfd5e3e938f55314981ac59790 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -2292,6 +2292,11 @@ private:
   int              m_BIFStrength;
   int              m_BIFQPOffset;
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  bool             m_CBIF;
+  int              m_CBIFStrength;
+  int              m_CBIFQPOffset;
+#endif
 
 public:
   PreCalcValues   *pcv;
@@ -2494,6 +2499,14 @@ public:
   int                    getBIFStrength() const                                           { return m_BIFStrength;                         }
   void                   setBIFQPOffset( int val)                                         { m_BIFQPOffset = val;                          }
   int                    getBIFQPOffset() const                                           { return m_BIFQPOffset;                         }
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  void                   setUseCBIF( bool b)                                              { m_CBIF = b;             }
+  bool                   getUseCBIF() const                                               { return m_CBIF;          }
+  void                   setCBIFStrength( int val)                                        { m_CBIFStrength = val;   }
+  int                    getCBIFStrength() const                                          { return m_CBIFStrength;  }
+  void                   setCBIFQPOffset( int val)                                        { m_CBIFQPOffset = val;   }
+  int                    getCBIFQPOffset() const                                          { return m_CBIFQPOffset;  }
 #endif
   void                   setDeblockingFilterControlPresentFlag( bool val )                { m_deblockingFilterControlPresentFlag = val;   }
   bool                   getDeblockingFilterControlPresentFlag() const                    { return m_deblockingFilterControlPresentFlag;  }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 1c0334629cfd35e5ce18037ed6f5ea9b8a37845f..2bdf812acbe04cba61aa4419651a11862a850247 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -1,4 +1,4 @@
-/* The copyright in this software is being made available under the BSD
+/* 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.
@@ -157,6 +157,9 @@
 #define EMBEDDED_APS                                      1 // Embed APS into picture header
 #define JVET_V0094_BILATERAL_FILTER                       1 // Bilateral filter
 #define JVET_W0066_CCSAO                                  1 // JVET-W0066: Cross-component sample adaptive offset
+#define JVET_X0071_LONGER_CCALF                           1 // JVET-X0071/JVET-X0045: Longer filter for CCALF
+#define JVET_X0071_ALF_BAND_CLASSIFIER                    1 // JVET-X0071/JVET-X0070: Alternative band classifier for ALF
+#define JVET_X0071_CHROMA_BILATERAL_FILTER                1 // JVET-X0071/JVET-X0067: Chroma bilateral filter
 
 // SIMD optimizations
 #if IF_12TAP
@@ -174,6 +177,9 @@
 #if JVET_V0094_BILATERAL_FILTER
 #define ENABLE_SIMD_BILATERAL_FILTER                      1
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+#define JVET_X0071_CHROMA_BILATERAL_FILTER_ENABLE_SIMD    1
+#endif
 #endif // tools
 
 
@@ -1215,6 +1221,19 @@ public:
   std::vector<int> ctuOn;   // bif_ctb_flag[][]
 };
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+class CBifParams
+{
+public:
+    int frmOn_Cb;                // slice_bif_enabled_flag for chroma
+    int frmOn_Cr;                // slice_bif_enabled_flag for chroma
+    int allCtuOn_Cb;             // slice_bif_all_ctb_enabled_flag for chroma
+    int allCtuOn_Cr;             // slice_bif_all_ctb_enabled_flag for chroma
+    int numBlocks;
+    std::vector<int> ctuOn_Cb;   // bif_ctb_flag[][] for chroma
+    std::vector<int> ctuOn_Cr;   // bif_ctb_flag[][] for chroma
+};
+#endif
 
 
 struct BitDepths
diff --git a/source/Lib/CommonLib/x86/AdaptiveLoopFilterX86.h b/source/Lib/CommonLib/x86/AdaptiveLoopFilterX86.h
index a0a4652532432a7a4ce50ab62db9aa2c1365396b..84eb1385d56df3325681338bc9f23068932fe6ad 100644
--- a/source/Lib/CommonLib/x86/AdaptiveLoopFilterX86.h
+++ b/source/Lib/CommonLib/x86/AdaptiveLoopFilterX86.h
@@ -1942,7 +1942,11 @@ static void simdCalcClass0(AlfClassifier **classifier, const Area &blkDst, const
 static void simdCalcClass1(AlfClassifier **classifier, const Area &blkDst, const Area &curBlk, int dirWindSize, int classDir, int noDir, int noAct, int bitDepth, int subBlkSize, int mappingDir[NUM_DIR_FIX][NUM_DIR_FIX], uint32_t **laplacian[NUM_DIRECTIONS])
 {
   const __m128i shift = _mm_cvtsi32_si128(9 + bitDepth);
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  const int multTab[] = { 16884, 4221, 1872, 1053, 675, 468 };
+#else
   const int multTab[] = { 5628, 1407, 624, 351, 225, 156 };
+#endif
   const __m128i mult = _mm_set1_epi32(multTab[dirWindSize]);
   const __m128i scale = _mm_set1_epi32(15);
 
diff --git a/source/Lib/CommonLib/x86/BilateralFilterX86.h b/source/Lib/CommonLib/x86/BilateralFilterX86.h
index 7af1e6828bc63391de20274f0ecda1513d01549f..d31526e6cbf1f5d6699d6798e7769e21fe297a41 100644
--- a/source/Lib/CommonLib/x86/BilateralFilterX86.h
+++ b/source/Lib/CommonLib/x86/BilateralFilterX86.h
@@ -43,7 +43,7 @@
 #include <x86intrin.h>
 #endif
 
-#if ENABLE_SIMD_BILATERAL_FILTER
+#if ENABLE_SIMD_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER_ENABLE_SIMD
 #if JVET_W0066_CCSAO
 template<X86_VEXT vext>
 void BilateralFilter::simdFilterDiamond5x5NoClip(uint32_t uiWidth, uint32_t uiHeight, int16_t block[], int16_t blkFilt[], const ClpRng& clpRng, Pel* recPtr, int recStride, int iWidthExtSIMD, int bfac, int bif_round_add, int bif_round_shift, bool isRDO, const char* LUTrowPtr)
diff --git a/source/Lib/CommonLib/x86/InitX86.cpp b/source/Lib/CommonLib/x86/InitX86.cpp
index aa4c474abc0ad75a2afa32bb494e28b7e9da75f2..e1280950883219a735656d522c6ff3427d94b704 100644
--- a/source/Lib/CommonLib/x86/InitX86.cpp
+++ b/source/Lib/CommonLib/x86/InitX86.cpp
@@ -53,7 +53,7 @@
 #if ENABLE_SIMD_SIGN_PREDICTION || TRANSFORM_SIMD_OPT || ENABLE_SIMD_TMP
 #include "CommonLib/TrQuant.h"
 #endif
-#if ENABLE_SIMD_BILATERAL_FILTER
+#if ENABLE_SIMD_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER_ENABLE_SIMD
 #include "CommonLib/BilateralFilter.h"
 #endif
 
@@ -216,7 +216,7 @@ void TrQuant::initTrQuantX86()
 }
 #endif
 
-#if ENABLE_SIMD_BILATERAL_FILTER
+#if ENABLE_SIMD_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER_ENABLE_SIMD
 void BilateralFilter::initBilateralFilterX86()
 {
   auto vext = read_x86_extension_flags();
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 5c27649f65470681c523ed7b1e914feabf6c4e05..2fedef749bca2d9aa5bf468db425be5b7969665c 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -327,11 +327,19 @@ void CABACReader::ccAlfFilterControlIdc(CodingStructure &cs, const ComponentID c
 
   if (leftAvail)
   {
+#if JVET_X0071_LONGER_CCALF
+    ctxt += (filterControlIdc[curIdx - 1] != 1) ? 1 : 0;
+#else
     ctxt += ( filterControlIdc[curIdx - 1] ) ? 1 : 0;
+#endif
   }
   if (aboveAvail)
   {
+#if JVET_X0071_LONGER_CCALF
+    ctxt += (filterControlIdc[curIdx - cs.pcv->widthInCtus] != 1) ? 1 : 0;
+#else
     ctxt += ( filterControlIdc[curIdx - cs.pcv->widthInCtus] ) ? 1 : 0;
+#endif
   }
   ctxt += ( compID == COMPONENT_Cr ) ? 3 : 0;
 
@@ -343,6 +351,12 @@ void CABACReader::ccAlfFilterControlIdc(CodingStructure &cs, const ComponentID c
       idcVal++;
     }
   }
+
+#if JVET_X0071_LONGER_CCALF
+  int pos0 = 1;
+  idcVal = (idcVal == pos0 ? 0 : idcVal < pos0 ? idcVal + 1 : idcVal);
+#endif
+
   filterControlIdc[curIdx] = idcVal;
 
   DTRACE(g_trace_ctx, D_SYNTAX, "ccAlfFilterControlIdc() compID=%d pos=(%d,%d) ctxt=%d, filterCount=%d, idcVal=%d\n",
@@ -360,13 +374,25 @@ void CABACReader::sao( CodingStructure& cs, unsigned ctuRsAddr )
   const SPS&   sps   = *cs.sps;
 
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if(!(cs.pps->getUseBIF() || cs.sps->getSAOEnabledFlag() || !(cs.pps->getUseCBIF())))
+#else
   // If neither BIF nor SAO is enabled, we can return.
   if(!(cs.pps->getUseBIF() || cs.sps->getSAOEnabledFlag()))
+#endif
   {
     return;
   }
   // At least one is enabled, it is safe to assume we can do getSAO().
   SAOBlkParam&      sao_ctu_pars            = cs.picture->getSAO()[ctuRsAddr];
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if(!(cs.pps->getUseCBIF() || cs.sps->getSAOEnabledFlag()))
+  {
+        return;
+  }
+    SAOBlkParam&      sao_ctu_pars            = cs.picture->getSAO()[ctuRsAddr];
+#endif
 #endif
   
   if( !sps.getSAOEnabledFlag() )
@@ -375,7 +401,7 @@ void CABACReader::sao( CodingStructure& cs, unsigned ctuRsAddr )
   }
 
   const Slice& slice                        = *cs.slice;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   // The getSAO() function call has been moved up.
 #else
   SAOBlkParam&      sao_ctu_pars            = cs.picture->getSAO()[ctuRsAddr];
@@ -579,6 +605,139 @@ void CABACReader::bif(CodingStructure& cs, unsigned ctuRsAddr)
   }
 }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+void CABACReader::Cbif_Cb(CodingStructure& cs)
+{
+    int width = cs.picture->lwidth();
+    int height = cs.picture->lheight();
+
+    int block_width = cs.pcv->maxCUWidth;
+    int block_height = cs.pcv->maxCUHeight;
+
+    int width_in_blocks = width / block_width + (width % block_width != 0);
+    int height_in_blocks = height / block_height + (height % block_height != 0);
+
+    for (int i = 0; i < width_in_blocks * height_in_blocks; i++)
+    {
+        Cbif_Cb(cs, i);
+    }
+}
+
+void CABACReader::Cbif_Cb(CodingStructure& cs, unsigned ctuRsAddr)
+{
+    const PPS&   pps = *cs.pps;
+
+    if (!pps.getUseCBIF())
+    {
+        return;
+    }
+
+    CBifParams& CBifParams = cs.picture->getCBifParam();
+
+    if (ctuRsAddr == 0)
+    {
+        int width = cs.picture->lwidth();
+        int height = cs.picture->lheight();
+        int block_width = cs.pcv->maxCUWidth;
+        int block_height = cs.pcv->maxCUHeight;
+
+        int width_in_blocks = width / block_width + (width % block_width != 0);
+        int height_in_blocks = height / block_height + (height % block_height != 0);
+        int num_blocks = width_in_blocks * height_in_blocks;
+        CBifParams.numBlocks = num_blocks;
+        CBifParams.ctuOn_Cb.resize(num_blocks);
+        std::fill(CBifParams.ctuOn_Cb.begin(), CBifParams.ctuOn_Cb.end(), 0);
+    }
+    if (ctuRsAddr == 0)
+    {
+        CBifParams.allCtuOn_Cb = m_BinDecoder.decodeBinEP();
+        if (CBifParams.allCtuOn_Cb == 0)
+            CBifParams.frmOn_Cb = m_BinDecoder.decodeBinEP();
+    }
+    int i = ctuRsAddr;
+    if (CBifParams.allCtuOn_Cb)
+    {
+        CBifParams.ctuOn_Cb[i] = 1;
+    }
+    else
+    {
+        if (CBifParams.frmOn_Cb)
+        {
+            CBifParams.ctuOn_Cb[i] = m_BinDecoder.decodeBin(Ctx::CBifCtrlFlags_Cb());
+        }
+        else
+        {
+            CBifParams.ctuOn_Cb[i] = 0;
+        }
+    }
+}
+
+void CABACReader::Cbif_Cr(CodingStructure& cs)
+{
+    int width = cs.picture->lwidth();
+    int height = cs.picture->lheight();
+
+    int block_width = cs.pcv->maxCUWidth;
+    int block_height = cs.pcv->maxCUHeight;
+
+    int width_in_blocks = width / block_width + (width % block_width != 0);
+    int height_in_blocks = height / block_height + (height % block_height != 0);
+
+    for (int i = 0; i < width_in_blocks * height_in_blocks; i++)
+    {
+        Cbif_Cr(cs, i);
+    }
+}
+
+void CABACReader::Cbif_Cr(CodingStructure& cs, unsigned ctuRsAddr)
+{
+    const PPS&   pps = *cs.pps;
+
+    if (!pps.getUseCBIF())
+    {
+        return;
+    }
+
+    CBifParams& CBifParams = cs.picture->getCBifParam();
+
+    if (ctuRsAddr == 0)
+    {
+        int width = cs.picture->lwidth();
+        int height = cs.picture->lheight();
+        int block_width = cs.pcv->maxCUWidth;
+        int block_height = cs.pcv->maxCUHeight;
+
+        int width_in_blocks = width / block_width + (width % block_width != 0);
+        int height_in_blocks = height / block_height + (height % block_height != 0);
+        int num_blocks = width_in_blocks * height_in_blocks;
+        CBifParams.numBlocks = num_blocks;
+        CBifParams.ctuOn_Cr.resize(num_blocks);
+        std::fill(CBifParams.ctuOn_Cr.begin(), CBifParams.ctuOn_Cr.end(), 0);
+    }
+    if (ctuRsAddr == 0)
+    {
+        CBifParams.allCtuOn_Cr = m_BinDecoder.decodeBinEP();
+        if (CBifParams.allCtuOn_Cr == 0)
+            CBifParams.frmOn_Cr = m_BinDecoder.decodeBinEP();
+    }
+    int i = ctuRsAddr;
+    if (CBifParams.allCtuOn_Cr)
+    {
+        CBifParams.ctuOn_Cr[i] = 1;
+    }
+    else
+    {
+        if (CBifParams.frmOn_Cr)
+        {
+            CBifParams.ctuOn_Cr[i] = m_BinDecoder.decodeBin(Ctx::CBifCtrlFlags_Cr());
+        }
+        else
+        {
+            CBifParams.ctuOn_Cr[i] = 0;
+        }
+    }
+}
+#endif
 
 #if JVET_W0066_CCSAO
 void CABACReader::ccSaoControlIdc(CodingStructure &cs, const ComponentID compID, const int curIdx,
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index 479185d5ac0e0f54fa0122e4c265be2719edd40e..cc22363f7014cfc763f43cd2026286377a9f6b91 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -72,6 +72,12 @@ public:
   void        bif                      (CodingStructure&              cs);
   void        bif                      (CodingStructure&              cs, unsigned ctuRsAddr);
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  void        Cbif_Cb                      (CodingStructure&              cs);
+  void        Cbif_Cb                      (CodingStructure&              cs, unsigned ctuRsAddr);
+  void        Cbif_Cr                      (CodingStructure&              cs);
+  void        Cbif_Cr                      (CodingStructure&              cs, unsigned ctuRsAddr);
+#endif
   
 #if JVET_W0066_CCSAO
   void        ccSaoControlIdc           ( CodingStructure &cs, const ComponentID compID, const int curIdx, uint8_t *controlIdc, Position lumaPos, int setNum );
diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp
index 1a215327f017ae9325b02058890aeec63a92ea69..1cb97cdb728fef1111d6e477add049984bbf4265 100644
--- a/source/Lib/DecoderLib/DecLib.cpp
+++ b/source/Lib/DecoderLib/DecLib.cpp
@@ -193,11 +193,19 @@ bool tryDecodePicture( Picture* pcEncPic, const int expectedPoc, const std::stri
                 else
                 {
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+                if ( pic->cs->sps->getSAOEnabledFlag() || pic->cs->pps->getUseBIF() || pic->cs->pps->getUseCBIF())
+#else
                 // Since the per-CTU BIF parameter is stored in SAO, we need to
                 // do this copy even if SAO=0, if BIF=1.
                 if ( pic->cs->sps->getSAOEnabledFlag() || pic->cs->pps->getUseBIF() )
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+                if ( pic->cs->sps->getSAOEnabledFlag()  || pic->cs->pps->getUseCBIF())
 #else
                 if ( pic->cs->sps->getSAOEnabledFlag() )
+#endif
 #endif
                 {
                   pcEncPic->copySAO( *pic, 0 );
@@ -252,11 +260,19 @@ bool tryDecodePicture( Picture* pcEncPic, const int expectedPoc, const std::stri
 
                 pcDecLib->executeLoopFilters();
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+                if ( pic->cs->sps->getSAOEnabledFlag() || pic->cs->pps->getUseBIF() || pic->cs->pps->getUseCBIF())
+#else
                 // Since the per-CTU BIF parameter is stored in SAO, we need to
                 // do this copy even if SAO=0, if BIF=1.
                 if ( pic->cs->sps->getSAOEnabledFlag() || pic->cs->pps->getUseBIF() )
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+                if ( pic->cs->sps->getSAOEnabledFlag() || pic->cs->pps->getUseCBIF())
 #else
                 if ( pic->cs->sps->getSAOEnabledFlag() )
+#endif
 #endif
                 {
                   pcEncPic->copySAO( *pic, 1 );
@@ -518,7 +534,7 @@ void DecLib::create()
 {
   m_apcSlicePilot = new Slice;
   m_uiSliceSegmentIdx = 0;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_cBilateralFilter.create();
 #endif
 }
@@ -535,7 +551,7 @@ void DecLib::destroy()
   }
 
   m_cSliceDecoder.destroy();
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_cBilateralFilter.destroy();
 #endif
 }
@@ -701,9 +717,17 @@ void DecLib::executeLoopFilters()
 #endif
 
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if( cs.sps->getSAOEnabledFlag() || cs.pps->getUseBIF() || cs.pps->getUseCBIF())
+#else
   if( cs.sps->getSAOEnabledFlag() || cs.pps->getUseBIF())
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if( cs.sps->getSAOEnabledFlag() || cs.pps->getUseCBIF())
 #else
   if( cs.sps->getSAOEnabledFlag() )
+#endif
 #endif
   {
     m_cSAO.SAOProcess( cs, cs.picture->getSAO() );
diff --git a/source/Lib/DecoderLib/DecLib.h b/source/Lib/DecoderLib/DecLib.h
index 4f27ca0e61ed9edb00d11e7e413c0ebec3b823d8..0e50dd28a7be1603761c2f58c561c29509d554c1 100644
--- a/source/Lib/DecoderLib/DecLib.h
+++ b/source/Lib/DecoderLib/DecLib.h
@@ -54,7 +54,7 @@
 #include "CommonLib/SEI.h"
 #include "CommonLib/Unit.h"
 #include "CommonLib/Reshape.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "BilateralFilter.h"
 #endif
 
@@ -100,7 +100,7 @@ private:
 
   SEIMessages             m_SEIs; ///< List of SEI messages that have been received before the first slice and between slices, excluding prefix SEIs...
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter         m_cBilateralFilter;
 #endif
 
diff --git a/source/Lib/DecoderLib/DecSlice.cpp b/source/Lib/DecoderLib/DecSlice.cpp
index 106ffb682e8a38c90050c54399c86b210cf704bb..cca8fdb9da3f7d158b34907b5585462d49442c27 100644
--- a/source/Lib/DecoderLib/DecSlice.cpp
+++ b/source/Lib/DecoderLib/DecSlice.cpp
@@ -242,6 +242,13 @@ void DecSlice::decompressSlice( Slice* slice, InputBitstream* bitstream, int deb
     {
       cabacReader.bif(cs);
     }
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if (ctuRsAddr == 0)
+    {
+        cabacReader.Cbif_Cb(cs);
+        cabacReader.Cbif_Cr(cs);
+    }
 #endif
     cabacReader.coding_tree_unit( cs, ctuArea, pic->m_prevQP, ctuRsAddr );
 
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index 4d372bd1681b308308d5b69216339afc33139470..a369af00e3dce06a2366d315caf0ec444941507d 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -810,6 +810,14 @@ void HLSyntaxReader::parsePPS( PPS* pcPPS )
     READ_SVLC( iCode, "bilateral_filter_qp_offset" );                  pcPPS->setBIFQPOffset( iCode);
   }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  READ_FLAG( uiCode, "chroma bilateral_filter_flag" );             pcPPS->setUseCBIF(uiCode != 0) ;
+  if(pcPPS->getUseCBIF())
+  {
+    READ_CODE( 2, uiCode, "chroma bilateral_filter_strength" );    pcPPS->setCBIFStrength( uiCode);
+    READ_SVLC( iCode, "chroma bilateral_filter_qp_offset" );       pcPPS->setCBIFQPOffset( iCode);
+  }
+#endif
   
 #if !JVET_S0132_HLS_REORDER
   READ_FLAG( uiCode, "weighted_pred_flag" );          // Use of Weighting Prediction (P_SLICE)
@@ -1079,6 +1087,10 @@ void HLSyntaxReader::parseAlfAps( APS* aps )
     param.filterType[CHANNEL_TYPE_LUMA] = code ? ALF_FILTER_9_EXT : ALF_FILTER_EXT;
     for (int altIdx = 0; altIdx < param.numAlternativesLuma; ++altIdx)
     {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      READ_FLAG(code, "alf_luma_classifier");
+      param.lumaClassifierIdx[altIdx] = code;
+#endif
       READ_FLAG(code, "alf_luma_clip");
       param.nonLinearFlag[CHANNEL_TYPE_LUMA][altIdx] = code ? true : false;
       READ_UVLC(code, "alf_luma_num_filters_signalled_minus1");
@@ -1086,7 +1098,11 @@ void HLSyntaxReader::parseAlfAps( APS* aps )
       if (param.numLumaFilters[altIdx] > 1)
       {
         const int length = ceilLog2(param.numLumaFilters[altIdx]);
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        for (int i = 0; i < ALF_NUM_CLASSES_CLASSIFIER[(int)param.lumaClassifierIdx[altIdx]]; i++)
+#else
         for (int i = 0; i < MAX_NUM_ALF_CLASSES; i++)
+#endif
         {
           READ_CODE(length, code, "alf_luma_coeff_delta_idx");
           param.filterCoeffDeltaIdx[altIdx][i] = code;
@@ -1188,7 +1204,7 @@ void HLSyntaxReader::parseAlfAps( APS* aps )
         short *coeff = ccAlfParam.ccAlfCoeff[ccIdx][filterIdx];
         // Filter coefficients
         for (int i = 0; i < alfShape.numCoeff - 1; i++)
-        {
+        {          
           READ_CODE(CCALF_BITS_PER_COEFF_LEVEL, code,
                     ccIdx == 0 ? "alf_cc_cb_mapped_coeff_abs" : "alf_cc_cr_mapped_coeff_abs");
           if (code == 0)
@@ -4144,6 +4160,7 @@ void HLSyntaxReader::parseSliceHeader (Slice* pcSlice, PicHeader* picHeader, Par
   {
     READ_FLAG(uiCode, "slice_alf_enabled_flag");
     pcSlice->setTileGroupAlfEnabledFlag(COMPONENT_Y, uiCode);
+
     int alfCbEnabledFlag = 0;
     int alfCrEnabledFlag = 0;
 
@@ -4199,6 +4216,7 @@ void HLSyntaxReader::parseSliceHeader (Slice* pcSlice, PicHeader* picHeader, Par
     {
       READ_FLAG(uiCode, "slice_cc_alf_cb_enabled_flag");
       pcSlice->setTileGroupCcAlfCbEnabledFlag(uiCode);
+
       filterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1] = (uiCode == 1) ? true : false;
       pcSlice->setTileGroupCcAlfCbApsId(-1);
       if (filterParam.ccAlfFilterEnabled[COMPONENT_Cb - 1])
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 0400cd715f2daf2b2b08caf7454bfc052df3e5bf..f9c8e4e4244c917ee1554b38b81806d55a12bfe9 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -178,7 +178,69 @@ void CABACWriter::bif(const Slice& slice, const BifParams& bifParams, unsigned c
   }
 }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+void CABACWriter::Cbif_Cb(const Slice& slice, const CBifParams& CBifParams)
+{
+    for (int i = 0; i < CBifParams.numBlocks; ++i)
+    {
+        Cbif_Cb(slice, CBifParams, i);
+    }
+}
+
+void CABACWriter::Cbif_Cb(const Slice& slice, const CBifParams& CBifParams, unsigned ctuRsAddr)
+{
+    const PPS& pps = *slice.getPPS();
+    if (!pps.getUseCBIF())
+    {
+        return;
+    }
+
+    if (ctuRsAddr == 0)
+    {
+        m_BinEncoder.encodeBinEP(CBifParams.allCtuOn_Cb);
+        if (CBifParams.allCtuOn_Cb == 0)
+        {
+            m_BinEncoder.encodeBinEP(CBifParams.frmOn_Cb);
+        }
+    }
+    if (CBifParams.allCtuOn_Cb == 0 && CBifParams.frmOn_Cb)
+    {
+        int i = ctuRsAddr;
+        m_BinEncoder.encodeBin(CBifParams.ctuOn_Cb[i], Ctx::CBifCtrlFlags_Cb());
+    }
+}
 
+void CABACWriter::Cbif_Cr(const Slice& slice, const CBifParams& CBifParams)
+{
+    for (int i = 0; i < CBifParams.numBlocks; ++i)
+    {
+        Cbif_Cr(slice, CBifParams, i);
+    }
+}
+
+void CABACWriter::Cbif_Cr(const Slice& slice, const CBifParams& CBifParams, unsigned ctuRsAddr)
+{
+    const PPS& pps = *slice.getPPS();
+    if (!pps.getUseCBIF())
+    {
+        return;
+    }
+
+    if (ctuRsAddr == 0)
+    {
+        m_BinEncoder.encodeBinEP(CBifParams.allCtuOn_Cr);
+        if (CBifParams.allCtuOn_Cr == 0)
+        {
+            m_BinEncoder.encodeBinEP(CBifParams.frmOn_Cr);
+        }
+    }
+    if (CBifParams.allCtuOn_Cr == 0 && CBifParams.frmOn_Cr)
+    {
+        int i = ctuRsAddr;
+        m_BinEncoder.encodeBin(CBifParams.ctuOn_Cr[i], Ctx::CBifCtrlFlags_Cr());
+    }
+}
+#endif
 
 //================================================================================
 //  clause 7.3.8.2
@@ -4616,6 +4678,54 @@ void CABACWriter::codeAlfCtuEnableFlag( CodingStructure& cs, uint32_t ctuRsAddr,
   }
 }
 
+
+#if JVET_X0071_LONGER_CCALF
+void CABACWriter::codeCcAlfFilterControlIdc(uint8_t idcVal, CodingStructure &cs, const ComponentID compID,
+  const int curIdx, const uint8_t *filterControlIdc, Position lumaPos,
+  const int filterCount)
+{
+  CHECK(idcVal > filterCount, "Filter index is too large");
+
+  const uint32_t curSliceIdx = cs.slice->getIndependentSliceIdx();
+  const uint32_t curTileIdx = cs.pps->getTileIdx(lumaPos);
+  Position       leftLumaPos = lumaPos.offset(-(int)cs.pcv->maxCUWidth, 0);
+  Position       aboveLumaPos = lumaPos.offset(0, -(int)cs.pcv->maxCUWidth);
+  bool           leftAvail = cs.getCURestricted(leftLumaPos, lumaPos, curSliceIdx, curTileIdx, CH_L) ? true : false;
+  bool           aboveAvail = cs.getCURestricted(aboveLumaPos, lumaPos, curSliceIdx, curTileIdx, CH_L) ? true : false;
+  int            ctxt = 0;
+
+  if (leftAvail)
+  {
+    ctxt += (filterControlIdc[curIdx - 1] != 1) ? 1 : 0;
+  }
+  if (aboveAvail)
+  {
+    ctxt += (filterControlIdc[curIdx - cs.pcv->widthInCtus] != 1) ? 1 : 0;
+  }
+  ctxt += (compID == COMPONENT_Cr) ? 3 : 0;
+
+  int       pos0 = 1;
+  unsigned  mappedIdc = (idcVal == 0 ? pos0 : idcVal <= pos0 ? idcVal - 1 : idcVal);
+
+  m_BinEncoder.encodeBin((mappedIdc == 0) ? 0 : 1, Ctx::CcAlfFilterControlFlag(ctxt)); // ON/OFF flag is context coded
+  if (mappedIdc > 0)
+  {
+    int val = (mappedIdc - 1);
+    while (val)
+    {
+      m_BinEncoder.encodeBinEP(1);
+      val--;
+    }
+    if (mappedIdc < filterCount)
+    {
+      m_BinEncoder.encodeBinEP(0);
+    }
+  }
+  DTRACE(g_trace_ctx, D_SYNTAX, "ccAlfFilterControlIdc() compID=%d pos=(%d,%d) ctxt=%d, filterCount=%d, idcVal=%d\n", compID, lumaPos.x, lumaPos.y, ctxt, filterCount, idcVal);
+}
+
+#else
+
 void CABACWriter::codeCcAlfFilterControlIdc(uint8_t idcVal, CodingStructure &cs, const ComponentID compID,
                                             const int curIdx, const uint8_t *filterControlIdc, Position lumaPos,
                                             const int filterCount)
@@ -4657,6 +4767,10 @@ void CABACWriter::codeCcAlfFilterControlIdc(uint8_t idcVal, CodingStructure &cs,
   DTRACE( g_trace_ctx, D_SYNTAX, "ccAlfFilterControlIdc() compID=%d pos=(%d,%d) ctxt=%d, filterCount=%d, idcVal=%d\n", compID, lumaPos.x, lumaPos.y, ctxt, filterCount, idcVal );
 }
 
+#endif
+
+
+
 void CABACWriter::code_unary_fixed( unsigned symbol, unsigned ctxId, unsigned unary_max, unsigned fixed )
 {
   bool unary = (symbol <= unary_max);
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index 7222db36ecfa06a2c8ed56277e8d809101802712..1a2755e923370a4c98ad9fcd68beb19515d117c9 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -84,6 +84,12 @@ public:
   void        bif                      (const Slice&                   slice, const BifParams& BifParams);
   void        bif                      (const Slice& slice, const BifParams& BifParams, unsigned ctuRsAddr);
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  void        Cbif_Cb                      (const Slice&                   slice, const CBifParams& CBifParams);
+  void        Cbif_Cb                      (const Slice& slice, const CBifParams& CBifParams, unsigned ctuRsAddr);
+  void        Cbif_Cr                      (const Slice&                   slice, const CBifParams& CBifParams);
+  void        Cbif_Cr                      (const Slice& slice, const CBifParams& CBifParams, unsigned ctuRsAddr);
+#endif
 #if JVET_W0066_CCSAO
   void        codeCcSaoControlIdc       ( uint8_t idcVal, CodingStructure &cs, const ComponentID compID, const int curIdx,
                                           const uint8_t *controlIdc, Position lumaPos, const int setNum );
diff --git a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp
index fdbb07c95dd89a17158f14cf009a581ed266b4da..b168a1e33f177bdafe045d0aa12d4c78fac7a646 100644
--- a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp
+++ b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp
@@ -509,8 +509,41 @@ void EncAdaptiveLoopFilter::create( const EncCfg* encCfg, const int picWidth, co
       std::fill_n( m_ctuAlternativeTmp[compIdx], m_numCTUsInPic, 0 );
     }
     ChannelType chType = toChannelType( ComponentID( compIdx ) );
+#if !JVET_X0071_ALF_BAND_CLASSIFIER
     int numClasses = compIdx ? 1 : MAX_NUM_ALF_CLASSES;
+#endif
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    int numClassifier = compIdx ? 1 : ALF_NUM_CLASSIFIER;
+    m_alfCovariance[compIdx] = new AlfCovariance****[m_filterShapes[chType].size()];
+    for( int i = 0; i != m_filterShapes[chType].size(); i++ )
+    {
+      m_alfCovariance[compIdx][i] = nullptr;
+      if( m_filterTypeTest[chType][m_filterShapes[chType][i].filterType] == false )
+      {
+        continue;
+      }
+      m_alfCovariance[compIdx][i] = new AlfCovariance***[m_numCTUsInPic];
+      int numFixedFilterSets = ( m_filterShapes[chType][i].filterType == ALF_FILTER_EXT || m_filterShapes[chType][i].filterType == ALF_FILTER_9_EXT ) ? 2 : 1;
+      for( int j = 0; j < m_numCTUsInPic; j++ )
+      {
+        m_alfCovariance[compIdx][i][j] = new AlfCovariance**[numFixedFilterSets];
+        for( int fixedFilterSetIdx = 0; fixedFilterSetIdx < numFixedFilterSets; fixedFilterSetIdx++ )
+        {
+          m_alfCovariance[compIdx][i][j][fixedFilterSetIdx] = new AlfCovariance*[numClassifier];
+          for( int l = 0; l < numClassifier; l++ )
+          {
+            int numClasses = compIdx ? 1 : ALF_NUM_CLASSES_CLASSIFIER[l];
+            m_alfCovariance[compIdx][i][j][fixedFilterSetIdx][l] = new AlfCovariance[numClasses];
+            for( int k = 0; k < numClasses; k++ )
+            {
+              m_alfCovariance[compIdx][i][j][fixedFilterSetIdx][l][k].create(m_filterShapes[chType][i].numCoeff);
+            }
+          }
+        }                           
+      }
+    }
+#else
     m_alfCovariance[compIdx] = new AlfCovariance***[m_filterShapes[chType].size()];
     for( int i = 0; i != m_filterShapes[chType].size(); i++ )
     {
@@ -534,6 +567,7 @@ void EncAdaptiveLoopFilter::create( const EncCfg* encCfg, const int picWidth, co
         }             
       }
     }
+#endif
 #else
     m_alfCovariance[compIdx] = new AlfCovariance**[m_filterShapes[chType].size()];
 
@@ -726,7 +760,9 @@ void EncAdaptiveLoopFilter::destroy()
     if( m_alfCovariance[compIdx] )
     {
       ChannelType chType = toChannelType( ComponentID( compIdx ) );
+#if !JVET_X0071_ALF_BAND_CLASSIFIER
       int numClasses = compIdx ? 1 : MAX_NUM_ALF_CLASSES;
+#endif
 
       for( int i = 0; i != m_filterShapes[chType].size(); i++ )
       {
@@ -740,6 +776,24 @@ void EncAdaptiveLoopFilter::destroy()
         for( int j = 0; j < m_numCTUsInPic; j++ )
         {
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          int numClassifier = compIdx ? 1 : ALF_NUM_CLASSIFIER;
+          for( int fixedFilterSetIdx = 0; fixedFilterSetIdx < numFixedFilterSet; fixedFilterSetIdx++ )
+          {
+            for( int l = 0; l < numClassifier; l++ )
+            {
+              int numClasses = compIdx ? 1 : ALF_NUM_CLASSES_CLASSIFIER[l];
+              for( int k = 0; k < numClasses; k++ )
+              {
+                m_alfCovariance[compIdx][i][j][fixedFilterSetIdx][l][k].destroy();
+              }
+              delete[] m_alfCovariance[compIdx][i][j][fixedFilterSetIdx][l];
+              m_alfCovariance[compIdx][i][j][fixedFilterSetIdx][l] = nullptr;
+            }
+            delete[] m_alfCovariance[compIdx][i][j][fixedFilterSetIdx];
+            m_alfCovariance[compIdx][i][j][fixedFilterSetIdx] = nullptr;
+          }
+#else
           for( int fixedFilterSetIdx = 0; fixedFilterSetIdx < numFixedFilterSet; fixedFilterSetIdx++ )
           {
             for( int k = 0; k < numClasses; k++ )
@@ -749,6 +803,7 @@ void EncAdaptiveLoopFilter::destroy()
             delete[] m_alfCovariance[compIdx][i][j][fixedFilterSetIdx];
             m_alfCovariance[compIdx][i][j][fixedFilterSetIdx] = nullptr;
           }
+#endif
 #else
           for( int k = 0; k < numClasses; k++ )
           {
@@ -1184,11 +1239,15 @@ void EncAdaptiveLoopFilter::ALFProcess( CodingStructure& cs, const double *lambd
 
             const Area blkSrc( 0, 0, w, h );
             const Area blkDst( xStart, yStart, w, h );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+            deriveClassification( m_classifier, buf.get( COMPONENT_Y ), blkDst, blkSrc, cs, -1, ALF_NUM_CLASSIFIER );
+#else
             deriveClassification( m_classifier, buf.get( COMPONENT_Y ), blkDst, blkSrc 
 #if ALF_IMPROVEMENT
               , cs, -1
 #endif
             );
+#endif
 
             xStart = xEnd;
           }
@@ -1199,11 +1258,15 @@ void EncAdaptiveLoopFilter::ALFProcess( CodingStructure& cs, const double *lambd
       else
       {
         Area blk( xPos, yPos, width, height );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        deriveClassification( m_classifier, recLuma, blk, blk , cs, -1, ALF_NUM_CLASSIFIER );
+#else
         deriveClassification( m_classifier, recLuma, blk, blk 
 #if ALF_IMPROVEMENT
           , cs, -1
 #endif
         );
+#endif
       }
     }
   }
@@ -1429,7 +1492,12 @@ double EncAdaptiveLoopFilter::deriveCtbAlfEnableFlags( CodingStructure& cs, cons
           m_ctuAlternative[compID][ctuIdx] = altIdx;
           m_CABACEstimator->codeAlfCtuAlternative(cs, ctuIdx, compID, &m_alfParamTemp, numAlts);
           double r_altCost = ctuLambda * FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          int classifierIdx = m_classifierFinal[altIdx];
+          double altDist = getFilteredDistortion(m_alfCovariance[compID][iShapeIdx][ctuIdx][fixedFilterSetIdx][classifierIdx], ALF_NUM_CLASSES_CLASSIFIER[classifierIdx], m_alfParamTemp.numLumaFilters[altIdx] - 1, numCoeff, altIdx);
+#else
           double altDist = getFilteredDistortion(m_alfCovariance[compID][iShapeIdx][ctuIdx][fixedFilterSetIdx], numClasses, m_alfParamTemp.numLumaFilters[altIdx] - 1, numCoeff, altIdx);
+#endif
           double altCost = altDist + r_altCost;
 
           if (altCost < bestAltCost)
@@ -1464,7 +1532,11 @@ double EncAdaptiveLoopFilter::deriveCtbAlfEnableFlags( CodingStructure& cs, cons
 
           double altDist = 0.;
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          altDist += m_alfCovariance[compID][iShapeIdx][ctuIdx][0][0][0].calcErrorForCoeffs( m_filterClippSet[altIdx], m_filterCoeffSet[altIdx], numCoeff, m_NUM_BITS );
+#else
           altDist += m_alfCovariance[compID][iShapeIdx][ctuIdx][0][0].calcErrorForCoeffs(  m_filterClippSet[altIdx], m_filterCoeffSet[altIdx], numCoeff, m_NUM_BITS );
+#endif
 #else
           altDist += m_alfCovariance[compID][iShapeIdx][ctuIdx][0].calcErrorForCoeffs(  m_filterClippSet[altIdx], m_filterCoeffSet[altIdx], numCoeff, m_NUM_BITS );
 #endif
@@ -1776,24 +1848,40 @@ double EncAdaptiveLoopFilter::getFilterCoeffAndCost( CodingStructure& cs, double
 #if ALF_IMPROVEMENT
     for (int altIdx = 0; altIdx < m_alfParamTemp.numAlternativesLuma; ++altIdx)
     {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      AlfParam bestSliceParam;
+      double bestCost = MAX_DOUBLE;
+      double bestDist = MAX_DOUBLE;
+      int bestCoeffBits = 0;
+      for( int classifierIdx = 0; classifierIdx < ALF_NUM_CLASSIFIER; classifierIdx++ )
+      {
+#endif
       //collect stat based on CTU decision
       if ( bReCollectStat )
       {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        getFrameStats( channel, iShapeIdx, altIdx, fixedFilterSetIdx, classifierIdx );
+#else
         getFrameStats( channel, iShapeIdx, altIdx 
 #if ALF_IMPROVEMENT
           , fixedFilterSetIdx
 #endif
         );
+#endif
       }
       assert(alfFilterShape.numCoeff == m_alfCovarianceFrame[channel][iShapeIdx][0].numCoeff);
-
+#if !JVET_X0071_ALF_BAND_CLASSIFIER
       AlfParam bestSliceParam;
       double bestCost = MAX_DOUBLE;
       double bestDist = MAX_DOUBLE;
       int bestCoeffBits = 0;
+#endif
       const int nonLinearFlagMax = m_encCfg->getUseNonLinearAlfLuma() ? 2 : 1;
       for (int nonLinearFlag = 0; nonLinearFlag < nonLinearFlagMax; nonLinearFlag++)
       {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        m_alfParamTemp.lumaClassifierIdx[altIdx] = classifierIdx;
+#endif
         m_alfParamTemp.nonLinearFlag[channel][altIdx] = nonLinearFlag;
         std::fill_n(m_alfClipMerged[iShapeIdx][0][0], MAX_NUM_ALF_LUMA_COEFF*MAX_NUM_ALF_CLASSES*MAX_NUM_ALF_CLASSES, m_alfParamTemp.nonLinearFlag[channel][altIdx] ? AlfNumClippingValues[CHANNEL_TYPE_LUMA] / 2 : 0);
 
@@ -1801,7 +1889,11 @@ double EncAdaptiveLoopFilter::getFilterCoeffAndCost( CodingStructure& cs, double
         m_alfCovarianceMerged[iShapeIdx][MAX_NUM_ALF_CLASSES].reset(AlfNumClippingValues[channel]);
         m_alfCovarianceMerged[iShapeIdx][MAX_NUM_ALF_CLASSES + 1].reset(AlfNumClippingValues[channel]);
         int curCoeffBits;
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        double curDist = mergeFiltersAndCost(m_alfParamTemp, alfFilterShape, m_alfCovarianceFrame[channel][iShapeIdx], m_alfCovarianceMerged[iShapeIdx], m_alfClipMerged[iShapeIdx], curCoeffBits, altIdx, classifierIdx, m_alfParamTemp.numLumaFilters[altIdx]);
+#else
         double curDist = mergeFiltersAndCost(m_alfParamTemp, alfFilterShape, m_alfCovarianceFrame[channel][iShapeIdx], m_alfCovarianceMerged[iShapeIdx], m_alfClipMerged[iShapeIdx], curCoeffBits, altIdx);
+#endif
         double cost = curDist + m_lambda[channel] * curCoeffBits;
         if (cost < bestCost)
         {
@@ -1811,6 +1903,9 @@ double EncAdaptiveLoopFilter::getFilterCoeffAndCost( CodingStructure& cs, double
           bestSliceParam = m_alfParamTemp;
         }
       }//for (int nonLinearFlag)
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      }//for(int classifierIdx)
+#endif
       uiCoeffBits += bestCoeffBits;
       dist += bestDist;
       m_alfParamTemp = bestSliceParam;
@@ -1841,11 +1936,15 @@ double EncAdaptiveLoopFilter::getFilterCoeffAndCost( CodingStructure& cs, double
       //collect stat based on CTU decision
       if ( bReCollectStat )
       {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        getFrameStats( channel, iShapeIdx, altIdx, fixedFilterSetIdx, 0 );
+#else
         getFrameStats( channel, iShapeIdx, altIdx 
 #if ALF_IMPROVEMENT
           , fixedFilterSetIdx
 #endif
         );
+#endif
       }
       assert(alfFilterShape.numCoeff == m_alfCovarianceFrame[channel][iShapeIdx][0].numCoeff);
 
@@ -2020,32 +2119,72 @@ double EncAdaptiveLoopFilter::getFilteredDistortion( AlfCovariance* cov, const i
   return dist;
 }
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+double EncAdaptiveLoopFilter::mergeFiltersAndCost( AlfParam& alfParam, AlfFilterShape& alfShape, AlfCovariance* covFrame, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], int& uiCoeffBits, int altIdx, int classifierIdx, int numFiltersLinear )
+#else
 double EncAdaptiveLoopFilter::mergeFiltersAndCost( AlfParam& alfParam, AlfFilterShape& alfShape, AlfCovariance* covFrame, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], int& uiCoeffBits 
 #if ALF_IMPROVEMENT
   , int altIdx
 #endif
 )
+#endif
 {
   int numFiltersBest = 0;
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  int numFilters = ALF_NUM_CLASSES_CLASSIFIER[classifierIdx];
+#else
   int numFilters = MAX_NUM_ALF_CLASSES;
+#endif
   bool   codedVarBins[MAX_NUM_ALF_CLASSES];
   double errorForce0CoeffTab[MAX_NUM_ALF_CLASSES][2];
   double cost, cost0, dist, distForce0, costMin = MAX_DOUBLE;
   int coeffBits, coeffBitsForce0;
-
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  int maxNumFilters = m_alfParamTemp.nonLinearFlag[CHANNEL_TYPE_LUMA][altIdx] ? std::min( numFiltersLinear + 3, numFilters ) : numFilters;
+  int bestCoeff[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF];
+  int bestClipp[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF];
+  double bestDist = 0;
+  int bestBits = 0;
+  bool   bestCodedVarBins[MAX_NUM_ALF_CLASSES] = { false };
+  bool   bestAlfLumaCoeffDeltaFlag = false;
+  static int mergedPair[MAX_NUM_ALF_CLASSES][2] = { 0 };
+  int mergedCoeff[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF] = { 0 };
+  double mergedErr[MAX_NUM_ALF_CLASSES] = { 0 };
+  if( m_alfParamTemp.nonLinearFlag[CHANNEL_TYPE_LUMA][altIdx] == false )
+  {
+    memset( mergedPair, 0, sizeof( mergedPair ) );
+    mergeClasses( alfShape, covFrame, covMerged, clipMerged, numFilters, m_filterIndices, altIdx, mergedPair );
+  }
+  else
+  {
+    for (int i = 0; i < numFilters; i++)
+    {
+      for (int j = 0; j < numFilters; j++)
+      {
+        std::fill_n(clipMerged[i][j], MAX_NUM_ALF_LUMA_COEFF, 2);
+      }
+    }
+  }
+  numFilters = maxNumFilters;
+#else
   mergeClasses( alfShape, covFrame, covMerged, clipMerged, MAX_NUM_ALF_CLASSES, m_filterIndices 
 #if ALF_IMPROVEMENT
     , altIdx
 #endif
   );
+#endif
 
   while( numFilters >= 1 )
   {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    dist = deriveFilterCoeffs(covFrame, covMerged, clipMerged, alfShape, m_filterIndices[numFilters - 1], numFilters, errorForce0CoeffTab, alfParam, m_alfParamTemp.nonLinearFlag[CHANNEL_TYPE_LUMA][altIdx], classifierIdx, numFilters == maxNumFilters ? true : false, mergedPair, mergedCoeff, mergedErr);
+#else
     dist = deriveFilterCoeffs(covFrame, covMerged, clipMerged, alfShape, m_filterIndices[numFilters - 1], numFilters, errorForce0CoeffTab, alfParam
 #if ALF_IMPROVEMENT
       , m_alfParamTemp.nonLinearFlag[CHANNEL_TYPE_LUMA][altIdx]
 #endif
     );
+#endif
     // filter coeffs are stored in m_filterCoeffSet
     distForce0 = getDistForce0( alfShape, numFilters, errorForce0CoeffTab, codedVarBins 
 #if ALF_IMPROVEMENT
@@ -2062,19 +2201,61 @@ double EncAdaptiveLoopFilter::mergeFiltersAndCost( AlfParam& alfParam, AlfFilter
     cost = dist + m_lambda[COMPONENT_Y] * coeffBits;
     cost0 = distForce0 + m_lambda[COMPONENT_Y] * coeffBitsForce0;
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    bool cost0better = false;
+#endif
+
     if( cost0 < cost )
     {
       cost = cost0;
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      cost0better = true;
+#endif
     }
 
     if( cost <= costMin )
     {
       costMin = cost;
       numFiltersBest = numFilters;
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      memcpy( bestCodedVarBins, codedVarBins, sizeof( codedVarBins ) );
+      for( int varInd = 0; varInd < numFilters; varInd++ )
+      {
+        if( cost0better && ( !bestCodedVarBins[varInd] ) )
+        {
+          memset( bestCoeff[varInd], 0, sizeof(int)*MAX_NUM_ALF_LUMA_COEFF );
+          memset( bestClipp[varInd], 0, sizeof(int)*MAX_NUM_ALF_LUMA_COEFF );
+        }
+        else
+        {
+          memcpy( bestCoeff[varInd], m_filterCoeffSet[varInd], sizeof(int)*MAX_NUM_ALF_LUMA_COEFF );
+          memcpy( bestClipp[varInd], m_filterClippSet[varInd], sizeof(int)*MAX_NUM_ALF_LUMA_COEFF );
+        }
+      }
+      if( cost0better )
+      {
+        bestDist = distForce0;
+        bestBits = coeffBitsForce0;
+        bestAlfLumaCoeffDeltaFlag = 1;
+      }
+      else
+      {
+        bestDist = dist;
+        bestBits = coeffBits;
+        bestAlfLumaCoeffDeltaFlag = 0;
+      }
+#endif
     }
     numFilters--;
   }
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  double distReturn = bestDist;
+  uiCoeffBits = bestBits;
+  alfParam.alfLumaCoeffDeltaFlag = bestAlfLumaCoeffDeltaFlag;
+  memcpy( alfParam.alfLumaCoeffFlag, bestCodedVarBins, sizeof( bestCodedVarBins ) );
+  alfParam.numLumaFilters[altIdx] = numFiltersBest;
+#else
   dist = deriveFilterCoeffs( covFrame, covMerged, clipMerged, alfShape, m_filterIndices[numFiltersBest - 1], numFiltersBest, errorForce0CoeffTab, alfParam 
 #if ALF_IMPROVEMENT
     , m_alfParamTemp.nonLinearFlag[CHANNEL_TYPE_LUMA][altIdx]
@@ -2122,17 +2303,27 @@ double EncAdaptiveLoopFilter::mergeFiltersAndCost( AlfParam& alfParam, AlfFilter
       }
     }
   }
+#endif
 #if ALF_IMPROVEMENT
   for (int ind = 0; ind < alfParam.numLumaFilters[altIdx]; ++ind)
   {
     for (int i = 0; i < alfShape.numCoeff; i++)
     {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      alfParam.lumaCoeff[altIdx][ind * MAX_NUM_ALF_LUMA_COEFF + i] = bestCoeff[ind][i];
+      alfParam.lumaClipp[altIdx][ind * MAX_NUM_ALF_LUMA_COEFF + i] = bestClipp[ind][i];
+#else
       alfParam.lumaCoeff[altIdx][ind * MAX_NUM_ALF_LUMA_COEFF + i] = m_filterCoeffSet[ind][i];
       alfParam.lumaClipp[altIdx][ind * MAX_NUM_ALF_LUMA_COEFF + i] = m_filterClippSet[ind][i];
+#endif
     }
   }
   memcpy( alfParam.filterCoeffDeltaIdx[altIdx], m_filterIndices[numFiltersBest - 1], sizeof(short) * MAX_NUM_ALF_CLASSES );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  uiCoeffBits += getNonFilterCoeffRate(alfParam, altIdx, classifierIdx);
+#else
   uiCoeffBits += getNonFilterCoeffRate(alfParam, altIdx);
+#endif
 #else
   for( int ind = 0; ind < alfParam.numLumaFilters; ++ind )
   {
@@ -2149,11 +2340,15 @@ double EncAdaptiveLoopFilter::mergeFiltersAndCost( AlfParam& alfParam, AlfFilter
   return distReturn;
 }
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+int EncAdaptiveLoopFilter::getNonFilterCoeffRate( AlfParam& alfParam, int altIdx, int classifierIdx )
+#else
 int EncAdaptiveLoopFilter::getNonFilterCoeffRate( AlfParam& alfParam 
 #if ALF_IMPROVEMENT
   , int altIdx
 #endif
 )
+#endif
 {
 #if ALF_IMPROVEMENT
   CHECK( alfParam.numLumaFilters[altIdx] < 1, "Wrong number of alfParam.numLumaFilters[altIdx]" );
@@ -2175,11 +2370,18 @@ int EncAdaptiveLoopFilter::getNonFilterCoeffRate( AlfParam& alfParam
   {
     const int coeffLength = ceilLog2(alfParam.numLumaFilters);
 #endif
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    for( int i = 0; i < ALF_NUM_CLASSES_CLASSIFIER[classifierIdx]; i++ )
+#else
     for( int i = 0; i < MAX_NUM_ALF_CLASSES; i++ )
+#endif
     {
       len += coeffLength;                              // alf_luma_coeff_delta_idx   u(v)
     }
   }
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  len++;
+#endif
   return len;
 }
 
@@ -2478,20 +2680,35 @@ int EncAdaptiveLoopFilter::lengthGolomb(int coeffVal, int k, bool signed_coeff)
 }
 #endif
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+double EncAdaptiveLoopFilter::deriveFilterCoeffs( AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], AlfFilterShape& alfShape, short* filterIndices, int numFilters, double errorTabForce0Coeff[MAX_NUM_ALF_CLASSES][2], AlfParam& alfParam, bool nonLinear, int classifierIdx, bool isMaxNum, int mergedPair[MAX_NUM_ALF_CLASSES][2], int mergedCoeff[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], double mergedErr[MAX_NUM_ALF_CLASSES] )
+#else
 double EncAdaptiveLoopFilter::deriveFilterCoeffs( AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], AlfFilterShape& alfShape, short* filterIndices, int numFilters, double errorTabForce0Coeff[MAX_NUM_ALF_CLASSES][2], AlfParam& alfParam 
 #if ALF_IMPROVEMENT
   , bool nonLinear
 #endif
 )
+#endif
 {
   double error = 0.0;
   AlfCovariance& tmpCov = covMerged[MAX_NUM_ALF_CLASSES];
-
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  int changedClass = -1;
+  if( !isMaxNum )
+  {
+    memcpy( clipMerged[numFilters - 1], clipMerged[numFilters], sizeof( int[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF] ) );
+  }
+#endif
   for( int filtIdx = 0; filtIdx < numFilters; filtIdx++ )
   {
     tmpCov.reset();
     bool found_clip = false;
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    bool changedFilter = isMaxNum;
+    for( int classIdx = 0; classIdx < ALF_NUM_CLASSES_CLASSIFIER[classifierIdx]; classIdx++ )
+#else
     for( int classIdx = 0; classIdx < MAX_NUM_ALF_CLASSES; classIdx++ )
+#endif
     {
       if( filterIndices[classIdx] == filtIdx )
       {
@@ -2499,7 +2716,20 @@ double EncAdaptiveLoopFilter::deriveFilterCoeffs( AlfCovariance* cov, AlfCovaria
         if( !found_clip )
         {
           found_clip = true; // clip should be at the adress of shortest one
-          memcpy(m_filterClippSet[filtIdx], clipMerged[numFilters-1][classIdx], sizeof(int[MAX_NUM_ALF_LUMA_COEFF]));
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          if( changedFilter == false && mergedPair[numFilters][0] == classIdx )
+          {
+            changedFilter = true;
+            if( nonLinear )
+            {
+              std::fill_n(clipMerged[numFilters - 1][classIdx], MAX_NUM_ALF_LUMA_COEFF, 2);
+            }
+          }
+          changedClass = classIdx;
+          memcpy( m_filterCoeffSet[filtIdx], mergedCoeff[classIdx], sizeof( int[MAX_NUM_ALF_LUMA_COEFF] ) );
+          errorTabForce0Coeff[filtIdx][1] = mergedErr[classIdx];
+#endif
+          memcpy(m_filterClippSet[filtIdx], clipMerged[numFilters - 1][classIdx], sizeof(int[MAX_NUM_ALF_LUMA_COEFF]));
         }
       }
     }
@@ -2507,7 +2737,20 @@ double EncAdaptiveLoopFilter::deriveFilterCoeffs( AlfCovariance* cov, AlfCovaria
     // Find coeffcients
     assert(alfShape.numCoeff == tmpCov.numCoeff);
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    if( changedFilter )
+    {
+      errorTabForce0Coeff[filtIdx][1] = tmpCov.pixAcc + deriveCoeffQuant( m_filterClippSet[filtIdx], m_filterCoeffSet[filtIdx], tmpCov, alfShape, m_NUM_BITS, nonLinear );
+      if( nonLinear )
+      {
+        memcpy( clipMerged[numFilters - 1][changedClass], m_filterClippSet[filtIdx], sizeof( int[MAX_NUM_ALF_LUMA_COEFF] ) );
+      }
+      memcpy( mergedCoeff[changedClass], m_filterCoeffSet[filtIdx], sizeof( int[MAX_NUM_ALF_LUMA_COEFF] ) );
+      mergedErr[changedClass] = errorTabForce0Coeff[filtIdx][1];
+    }
+#else
     errorTabForce0Coeff[filtIdx][1] = tmpCov.pixAcc + deriveCoeffQuant( m_filterClippSet[filtIdx], m_filterCoeffSet[filtIdx], tmpCov, alfShape, m_NUM_BITS, nonLinear );
+#endif
 #else
     errorTabForce0Coeff[filtIdx][1] = tmpCov.pixAcc + deriveCoeffQuant( m_filterClippSet[filtIdx], m_filterCoeffSet[filtIdx], tmpCov, alfShape, m_NUM_BITS, false );
 #endif
@@ -2605,12 +2848,15 @@ void EncAdaptiveLoopFilter::roundFiltCoeffCCALF(int16_t *filterCoeffQuant, doubl
     filterCoeffQuant[i] = CCALF_SMALL_TAB[best_index] * sign;
   }
 }
-
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+void EncAdaptiveLoopFilter::mergeClasses( const AlfFilterShape& alfShape, AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], const int numClasses, short filterIndices[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES] , const int altIdx, int mergedPair[MAX_NUM_ALF_CLASSES][2] )
+#else
 void EncAdaptiveLoopFilter::mergeClasses( const AlfFilterShape& alfShape, AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], const int numClasses, short filterIndices[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES] 
 #if ALF_IMPROVEMENT
   , const int altIdx
 #endif
 )
+#endif
 {
   int     tmpClip[MAX_NUM_ALF_LUMA_COEFF];
   int     bestMergeClip[MAX_NUM_ALF_LUMA_COEFF];
@@ -2742,7 +2988,22 @@ void EncAdaptiveLoopFilter::mergeClasses( const AlfFilterShape& alfShape, AlfCov
       classChanged[bestToMergeIdx1][i] = classChanged[i][bestToMergeIdx1] = true;
     }
 #endif
-
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    mergedPair[numRemaining - 1][0] = bestToMergeIdx1;
+    mergedPair[numRemaining - 1][1] = bestToMergeIdx2;
+    if( numRemaining == 2 )
+    {
+      int ind = 0;
+      for (int i = 0; i < numClasses; i++)
+      {
+        if( availableClass[i] )
+        {
+          mergedPair[numRemaining - 2][ind] = i;
+          ind++;
+        }
+      }
+    }
+#endif
     for( int i = 0; i < numClasses; i++ )
     {
       if( indexList[i] == bestToMergeIdx2 )
@@ -2787,14 +3048,21 @@ void EncAdaptiveLoopFilter::mergeClasses( const AlfFilterShape& alfShape, AlfCov
     }
   }
 }
-
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+void EncAdaptiveLoopFilter::getFrameStats( ChannelType channel, int iShapeIdx, int altIdx, int fixedFilterSetIdx, int classifierIdx )
+#else
 void EncAdaptiveLoopFilter::getFrameStats( ChannelType channel, int iShapeIdx, int altIdx 
 #if ALF_IMPROVEMENT
   , int fixedFilterSetIdx
 #endif
 )
+#endif
 {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  int numClasses = isLuma(channel) ? ALF_NUM_CLASSES_CLASSIFIER[classifierIdx] : 1;
+#else
   int numClasses = isLuma( channel ) ? MAX_NUM_ALF_CLASSES : 1;
+#endif
 
   for (int i = 0; i < numClasses; i++)
   {
@@ -2803,13 +3071,21 @@ void EncAdaptiveLoopFilter::getFrameStats( ChannelType channel, int iShapeIdx, i
   if (isLuma(channel))
   {
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    getFrameStat( m_alfCovarianceFrame[CHANNEL_TYPE_LUMA][iShapeIdx], m_alfCovariance[COMPONENT_Y][iShapeIdx], m_ctuEnableFlag[COMPONENT_Y], m_ctuAlternative[COMPONENT_Y], numClasses, altIdx, fixedFilterSetIdx, classifierIdx );
+#else
     getFrameStat( m_alfCovarianceFrame[CHANNEL_TYPE_LUMA][iShapeIdx], m_alfCovariance[COMPONENT_Y][iShapeIdx], m_ctuEnableFlag[COMPONENT_Y], m_ctuAlternative[COMPONENT_Y], numClasses, altIdx, fixedFilterSetIdx );
+#endif
 #else
     getFrameStat( m_alfCovarianceFrame[CHANNEL_TYPE_LUMA][iShapeIdx], m_alfCovariance[COMPONENT_Y][iShapeIdx], m_ctuEnableFlag[COMPONENT_Y], nullptr, numClasses, altIdx );
 #endif
   }
   else
   {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    getFrameStat( m_alfCovarianceFrame[CHANNEL_TYPE_CHROMA][iShapeIdx], m_alfCovariance[COMPONENT_Cb][iShapeIdx], m_ctuEnableFlag[COMPONENT_Cb], m_ctuAlternative[COMPONENT_Cb], numClasses, altIdx, fixedFilterSetIdx, classifierIdx );
+    getFrameStat( m_alfCovarianceFrame[CHANNEL_TYPE_CHROMA][iShapeIdx], m_alfCovariance[COMPONENT_Cr][iShapeIdx], m_ctuEnableFlag[COMPONENT_Cr], m_ctuAlternative[COMPONENT_Cr], numClasses, altIdx, fixedFilterSetIdx, classifierIdx );
+#else
     getFrameStat( m_alfCovarianceFrame[CHANNEL_TYPE_CHROMA][iShapeIdx], m_alfCovariance[COMPONENT_Cb][iShapeIdx], m_ctuEnableFlag[COMPONENT_Cb], m_ctuAlternative[COMPONENT_Cb], numClasses, altIdx 
 #if ALF_IMPROVEMENT
       , fixedFilterSetIdx
@@ -2820,11 +3096,16 @@ void EncAdaptiveLoopFilter::getFrameStats( ChannelType channel, int iShapeIdx, i
       , fixedFilterSetIdx
 #endif
     );
+#endif
   }
 }
 
 #if ALF_IMPROVEMENT
-void EncAdaptiveLoopFilter::getFrameStat( AlfCovariance* frameCov, AlfCovariance*** ctbCov, uint8_t* ctbEnableFlags, uint8_t* ctbAltIdx, const int numClasses, int altIdx, int fixedFilterSetIdx)
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+void EncAdaptiveLoopFilter::getFrameStat( AlfCovariance* frameCov, AlfCovariance**** ctbCov, uint8_t* ctbEnableFlags, uint8_t* ctbAltIdx, const int numClasses, int altIdx, int fixedFilterSetIdx, int classifierIdx )
+#else
+void EncAdaptiveLoopFilter::getFrameStat( AlfCovariance* frameCov, AlfCovariance*** ctbCov, uint8_t* ctbEnableFlags, uint8_t* ctbAltIdx, const int numClasses, int altIdx, int fixedFilterSetIdx )
+#endif
 #else
 void EncAdaptiveLoopFilter::getFrameStat( AlfCovariance* frameCov, AlfCovariance** ctbCov, uint8_t* ctbEnableFlags, uint8_t* ctbAltIdx, const int numClasses, int altIdx )
 #endif
@@ -2841,7 +3122,11 @@ void EncAdaptiveLoopFilter::getFrameStat( AlfCovariance* frameCov, AlfCovariance
 #if ALF_IMPROVEMENT
         if (altIdx == ctbAltIdx[ctuIdx])
         {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          frameCov[classIdx] += ctbCov[ctuIdx][fixedFilterSetIdx][classifierIdx][classIdx];
+#else
           frameCov[classIdx] += ctbCov[ctuIdx][fixedFilterSetIdx][classIdx];
+#endif
         }
 #else
         if( isLuma( channel ) || altIdx == ctbAltIdx[ctuIdx] )
@@ -2864,7 +3149,13 @@ void EncAdaptiveLoopFilter::deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnit
   for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ )
   {
     const ComponentID compID = ComponentID( compIdx );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    for( int classifierIdx = 0; classifierIdx < (isLuma(compID) ? ALF_NUM_CLASSIFIER : 1); classifierIdx++ )
+    {
+      const int numClasses = isLuma(compID) ? ALF_NUM_CLASSES_CLASSIFIER[classifierIdx] : 1;
+#else
     const int numClasses = isLuma( compID ) ? MAX_NUM_ALF_CLASSES : 1;
+#endif
 
     for( int shape = 0; shape != m_filterShapes[toChannelType( compID )].size(); shape++ )
     {
@@ -2882,7 +3173,11 @@ void EncAdaptiveLoopFilter::deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnit
 #if ALF_IMPROVEMENT
           for( int fixedFilterSetIdx = 0; fixedFilterSetIdx < numFixedFilterSet; fixedFilterSetIdx++ )
           {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+            m_alfCovariance[compIdx][shape][ctuIdx][fixedFilterSetIdx][classifierIdx][classIdx].reset( AlfNumClippingValues[toChannelType(compID)] );
+#else
             m_alfCovariance[compIdx][shape][ctuIdx][fixedFilterSetIdx][classIdx].reset(AlfNumClippingValues[toChannelType(compID)]);
+#endif
           }
 #else
           m_alfCovariance[compIdx][shape][ctuIdx][classIdx].reset(AlfNumClippingValues[toChannelType( compID )]);
@@ -2890,6 +3185,9 @@ void EncAdaptiveLoopFilter::deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnit
         }
       }
     }
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    }
+#endif
   }
 
   // init Frame stats buffers
@@ -2982,7 +3280,14 @@ void EncAdaptiveLoopFilter::deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnit
 #if ALF_IMPROVEMENT
               for (int fixedFilterSetIdx = 0; fixedFilterSetIdx < ((m_filterShapes[chType][shape].filterType == ALF_FILTER_EXT || m_filterShapes[chType][shape].filterType == ALF_FILTER_9_EXT) ? 2 : 1); fixedFilterSetIdx++)
               {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+                for (int classifierIdx = 0; classifierIdx < (compIdx ? 1 : ALF_NUM_CLASSIFIER); classifierIdx++)
+                {
+                  getBlkStats<alfWSSD>( m_alfCovariance[compIdx][shape][ctuRsAddr][fixedFilterSetIdx][classifierIdx], m_filterShapes[chType][shape], compIdx ? nullptr : m_classifier[classifierIdx], org, orgStride, rec, recStride, compAreaDst, compArea, chType, fixedFilterSetIdx, classifierIdx );
+                }
+#else
                 getBlkStats<alfWSSD>( m_alfCovariance[compIdx][shape][ctuRsAddr][fixedFilterSetIdx], m_filterShapes[chType][shape], compIdx ? nullptr : m_classifier, org, orgStride, rec, recStride, compAreaDst, compArea, chType, fixedFilterSetIdx);
+#endif
               }
 #else
               getBlkStats( m_alfCovariance[compIdx][shape][ctuRsAddr], m_filterShapes[chType][shape], compIdx ? nullptr : m_classifier, org, orgStride, rec, recStride, compAreaDst, compArea, chType, ((compIdx == 0) ? m_alfVBLumaCTUHeight : m_alfVBChmaCTUHeight), (compIdx == 0) ? m_alfVBLumaPos : m_alfVBChmaPos );
@@ -3009,7 +3314,11 @@ void EncAdaptiveLoopFilter::deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnit
             for( int classIdx = 0; classIdx < numClasses; classIdx++ )
             {
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+              m_alfCovarianceFrame[chType][shape][isLuma( compID ) ? classIdx : 0] += m_alfCovariance[compIdx][shape][ctuRsAddr][0][0][classIdx];
+#else
               m_alfCovarianceFrame[chType][shape][isLuma( compID ) ? classIdx : 0] += m_alfCovariance[compIdx][shape][ctuRsAddr][0][classIdx];
+#endif
 #else
               m_alfCovarianceFrame[chType][shape][isLuma( compID ) ? classIdx : 0] += m_alfCovariance[compIdx][shape][ctuRsAddr][classIdx];
 #endif
@@ -3043,8 +3352,16 @@ void EncAdaptiveLoopFilter::deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnit
               continue;
             }
             for( int fixedFilterSetIdx = 0; fixedFilterSetIdx < ((m_filterShapes[chType][shape].filterType == ALF_FILTER_EXT || m_filterShapes[chType][shape].filterType == ALF_FILTER_9_EXT) ? 2 : 1); fixedFilterSetIdx++ )
+
             {
-              getBlkStats<alfWSSD>( m_alfCovariance[compIdx][shape][ctuRsAddr][fixedFilterSetIdx], m_filterShapes[chType][shape], compIdx ? nullptr : m_classifier, org, orgStride, rec, recStride, compArea, compArea, chType, fixedFilterSetIdx);
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+              for( int classifierIdx = 0; classifierIdx < (compIdx ? 1 : ALF_NUM_CLASSIFIER); classifierIdx++ )
+              {
+                getBlkStats<alfWSSD>( m_alfCovariance[compIdx][shape][ctuRsAddr][fixedFilterSetIdx][classifierIdx], m_filterShapes[chType][shape], compIdx ? nullptr : m_classifier[classifierIdx], org, orgStride, rec, recStride, compArea, compArea, chType, fixedFilterSetIdx, classifierIdx );
+              }
+#else
+              getBlkStats<alfWSSD>( m_alfCovariance[compIdx][shape][ctuRsAddr][fixedFilterSetIdx], m_filterShapes[chType][shape], compIdx ? nullptr : m_classifier, org, orgStride, rec, recStride, compArea, compArea, chType, fixedFilterSetIdx );
+#endif
             }
 #else
             getBlkStats( m_alfCovariance[compIdx][shape][ctuRsAddr], m_filterShapes[chType][shape], compIdx ? nullptr : m_classifier, org, orgStride, rec, recStride, compArea, compArea, chType, ( ( compIdx == 0 ) ? m_alfVBLumaCTUHeight : m_alfVBChmaCTUHeight ), ( compIdx == 0 ) ? m_alfVBLumaPos : m_alfVBChmaPos );
@@ -3053,7 +3370,11 @@ void EncAdaptiveLoopFilter::deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnit
           for( int classIdx = 0; classIdx < numClasses; classIdx++ )
           {
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+            m_alfCovarianceFrame[chType][shape][isLuma( compID ) ? classIdx : 0] += m_alfCovariance[compIdx][shape][ctuRsAddr][0][0][classIdx];
+#else
             m_alfCovarianceFrame[chType][shape][isLuma( compID ) ? classIdx : 0] += m_alfCovariance[compIdx][shape][ctuRsAddr][0][classIdx];
+#endif
 #else
             m_alfCovarianceFrame[chType][shape][isLuma( compID ) ? classIdx : 0] += m_alfCovariance[compIdx][shape][ctuRsAddr][classIdx];
 #endif
@@ -3074,7 +3395,11 @@ void EncAdaptiveLoopFilter::deriveStatsForFiltering( PelUnitBuf& orgYuv, PelUnit
 
 #if ALF_IMPROVEMENT
 template<bool m_alfWSSD>
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+void EncAdaptiveLoopFilter::getBlkStats( AlfCovariance* alfCovariance, const AlfFilterShape& shape, AlfClassifier** classifier, const Pel* org, const int orgStride, const Pel* rec, const int recStride, const CompArea& areaDst, const CompArea& area, const ChannelType channel, int fixedFilterSetIdx, int classifierIdx )
+#else
 void EncAdaptiveLoopFilter::getBlkStats( AlfCovariance* alfCovariance, const AlfFilterShape& shape, AlfClassifier** classifier, const Pel* org, const int orgStride, const Pel* rec, const int recStride, const CompArea& areaDst, const CompArea& area, const ChannelType channel, int fixedFilterSetIdx )
+#endif
 #else
 void EncAdaptiveLoopFilter::getBlkStats( AlfCovariance* alfCovariance, const AlfFilterShape& shape, AlfClassifier** classifier, Pel* org, const int orgStride, Pel* rec, const int recStride, const CompArea& areaDst, const CompArea& area, const ChannelType channel, int vbCTUHeight, int vbPos )
 #endif
@@ -3202,7 +3527,11 @@ void EncAdaptiveLoopFilter::getBlkStats( AlfCovariance* alfCovariance, const Alf
     rec += recStride;
   }
 
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  int numClasses = classifier ? ALF_NUM_CLASSES_CLASSIFIER[classifierIdx] : 1;
+#else
   int numClasses = classifier ? MAX_NUM_ALF_CLASSES : 1;
+#endif
   for( classIdx = 0; classIdx < numClasses; classIdx++ )
   {
     for( int k = 1; k < shape.numCoeff; k++ )
@@ -3525,7 +3854,11 @@ void  EncAdaptiveLoopFilter::initDistortion(
       {
         if (m_filterTypeTest[toChannelType((ComponentID)comp)][m_filterShapes[toChannelType((ComponentID)comp)][shapeIdx].filterType])
         {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          m_ctbDistortionUnfilter[comp][ctbIdx] = getUnfilteredDistortion(m_alfCovariance[comp][shapeIdx][ctbIdx][0][0], comp == 0 ? MAX_NUM_ALF_CLASSES : 1);
+#else
           m_ctbDistortionUnfilter[comp][ctbIdx] = getUnfilteredDistortion(m_alfCovariance[comp][shapeIdx][ctbIdx][0], comp == 0 ? MAX_NUM_ALF_CLASSES : 1);
+#endif
           m_unFiltDistCompnent[comp] += m_ctbDistortionUnfilter[comp][ctbIdx];
           break;
         }
@@ -3607,7 +3940,12 @@ void  EncAdaptiveLoopFilter::getDistNewFilter( AlfParam& alfParam )
   int numFixedFilterSet = (filterTypeCtb == ALF_FILTER_EXT || filterTypeCtb == ALF_FILTER_9_EXT) ? 2 : 1;
   for( int altIdx = 0; altIdx < alfParam.numAlternativesLuma; altIdx++ )
   {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+    int classifierIdx = m_classifierFinal[altIdx];
+    for( int classIdx = 0; classIdx < ALF_NUM_CLASSES_CLASSIFIER[classifierIdx]; classIdx++ )
+#else
     for( int classIdx = 0; classIdx < MAX_NUM_ALF_CLASSES; classIdx++ )
+#endif
     {
       for( int coeff = 0; coeff < MAX_NUM_ALF_LUMA_COEFF; coeff++ )
       {
@@ -3622,7 +3960,11 @@ void  EncAdaptiveLoopFilter::getDistNewFilter( AlfParam& alfParam )
           {
             m_distCtbLumaNewFilt[altIdx][fixedFilterSetIdx][ctbIdx] = m_ctbDistortionUnfilter[COMPONENT_Y][ctbIdx];
           }
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          m_distCtbLumaNewFilt[altIdx][fixedFilterSetIdx][ctbIdx] += m_alfCovariance[COMPONENT_Y][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeCtb]][ctbIdx][fixedFilterSetIdx][classifierIdx][classIdx].calcErrorForCoeffs( m_clipTmp, m_filterTmp, m_filterShapes[CHANNEL_TYPE_LUMA][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeCtb]].numCoeff, m_NUM_BITS );
+#else
           m_distCtbLumaNewFilt[altIdx][fixedFilterSetIdx][ctbIdx] += m_alfCovariance[COMPONENT_Y][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeCtb]][ctbIdx][fixedFilterSetIdx][classIdx].calcErrorForCoeffs(m_clipTmp, m_filterTmp, m_filterShapes[CHANNEL_TYPE_LUMA][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeCtb]].numCoeff, m_NUM_BITS);
+#endif
         }
       }
     }
@@ -3664,7 +4006,13 @@ void  EncAdaptiveLoopFilter::getDistApsFilter( CodingStructure& cs, std::vector<
     int numFixedFilterSet = ( filterTypeCtb == ALF_FILTER_EXT || filterTypeCtb == ALF_FILTER_9_EXT ) ? 2 : 1;
     for( int altIdx = 0; altIdx < alfParamTmp.numAlternativesLuma; altIdx++ )
     {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      int classifierIdx = m_classifierFinal[altIdx];
+      m_classifierIdxApsLuma[apsIdx][altIdx] = classifierIdx;
+      for( int classIdx = 0; classIdx < ALF_NUM_CLASSES_CLASSIFIER[classifierIdx]; classIdx++ )
+#else
       for( int classIdx = 0; classIdx < MAX_NUM_ALF_CLASSES; classIdx++ )
+#endif
       {
         for ( int coeff = 0; coeff < MAX_NUM_ALF_LUMA_COEFF; coeff++ )
         {
@@ -3679,7 +4027,11 @@ void  EncAdaptiveLoopFilter::getDistApsFilter( CodingStructure& cs, std::vector<
             {
               m_distCtbApsLuma[apsIdx][altIdx][fixedFilterSetIdx][ctbIdx] = m_ctbDistortionUnfilter[COMPONENT_Y][ctbIdx];
             }
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+            m_distCtbApsLuma[apsIdx][altIdx][fixedFilterSetIdx][ctbIdx] += m_alfCovariance[COMPONENT_Y][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeCtb]][ctbIdx][fixedFilterSetIdx][classifierIdx][classIdx].calcErrorForCoeffs(m_clipTmp, m_filterTmp, m_filterShapes[CHANNEL_TYPE_LUMA][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeCtb]].numCoeff, m_NUM_BITS);
+#else
             m_distCtbApsLuma[apsIdx][altIdx][fixedFilterSetIdx][ctbIdx] += m_alfCovariance[COMPONENT_Y][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeCtb]][ctbIdx][fixedFilterSetIdx][classIdx].calcErrorForCoeffs(m_clipTmp, m_filterTmp, m_filterShapes[CHANNEL_TYPE_LUMA][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeCtb]].numCoeff, m_NUM_BITS);
+#endif
           }          
         }
       }
@@ -3802,7 +4154,12 @@ void  EncAdaptiveLoopFilter::alfEncoderCtb(CodingStructure& cs, AlfParam& alfPar
                 int altIdx = m_ctuAlternative[COMPONENT_Y][ctbIdx];
 #endif
                 dDistOrgNewFilter += m_ctbDistortionUnfilter[COMPONENT_Y][ctbIdx];
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+                int classifierIdx = m_classifierFinal[altIdx];
+                for (int classIdx = 0; classIdx < ALF_NUM_CLASSES_CLASSIFIER[classifierIdx]; classIdx++)
+#else
                 for (int classIdx = 0; classIdx < MAX_NUM_ALF_CLASSES; classIdx++)
+#endif
                 {
 #if ALF_IMPROVEMENT
                   short* pCoeff = m_coeffFinal[altIdx];
@@ -3825,7 +4182,11 @@ void  EncAdaptiveLoopFilter::alfEncoderCtb(CodingStructure& cs, AlfParam& alfPar
                     m_clipTmp[i] = pClipp[classIdx * MAX_NUM_ALF_LUMA_COEFF + i];
                   }
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+                  dDistOrgNewFilter += m_alfCovariance[COMPONENT_Y][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeNewFilter]][ctbIdx][fixedFilterSetIdx][classifierIdx][classIdx].calcErrorForCoeffs(m_clipTmp, m_filterTmp, m_filterShapes[CHANNEL_TYPE_LUMA][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeNewFilter]].numCoeff, m_NUM_BITS);
+#else
                   dDistOrgNewFilter += m_alfCovariance[COMPONENT_Y][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeNewFilter]][ctbIdx][fixedFilterSetIdx][classIdx].calcErrorForCoeffs(m_clipTmp, m_filterTmp, m_filterShapes[CHANNEL_TYPE_LUMA][m_filterTypeToStatIndex[CHANNEL_TYPE_LUMA][filterTypeNewFilter]].numCoeff, m_NUM_BITS);
+#endif
 #else
                   dDistOrgNewFilter += m_alfCovariance[COMPONENT_Y][0][ctbIdx][classIdx].calcErrorForCoeffs(m_clipTmp, m_filterTmp, MAX_NUM_ALF_LUMA_COEFF, m_NUM_BITS);
 #endif
@@ -4229,7 +4590,11 @@ void  EncAdaptiveLoopFilter::alfEncoderCtb(CodingStructure& cs, AlfParam& alfPar
               m_clipTmp[i] = m_chromaClippFinal[altIdx][i];
             }
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+            double altDist = m_alfCovariance[compId][m_filterTypeToStatIndex[CHANNEL_TYPE_CHROMA][filterTypeChroma]][ctbIdx][0][0][0].calcErrorForCoeffs( m_clipTmp, m_filterTmp, m_filterShapes[CHANNEL_TYPE_CHROMA][m_filterTypeToStatIndex[CHANNEL_TYPE_CHROMA][filterTypeChroma]].numCoeff, m_NUM_BITS );
+#else
             double altDist = m_alfCovariance[compId][m_filterTypeToStatIndex[CHANNEL_TYPE_CHROMA][filterTypeChroma]][ctbIdx][0][0].calcErrorForCoeffs( m_clipTmp, m_filterTmp, m_filterShapes[CHANNEL_TYPE_CHROMA][m_filterTypeToStatIndex[CHANNEL_TYPE_CHROMA][filterTypeChroma]].numCoeff, m_NUM_BITS );
+#endif
 #else            
             double altDist = m_alfCovariance[compId][0][ctbIdx][0].calcErrorForCoeffs( m_clipTmp, m_filterTmp, MAX_NUM_ALF_CHROMA_COEFF, m_NUM_BITS );
 #endif
@@ -4430,7 +4795,12 @@ void EncAdaptiveLoopFilter::alfReconstructor(CodingStructure& cs, const PelUnitB
                 coeff = m_coeffApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
                 clip = m_clippApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
                 AlfFilterType filterTypeCtb = m_filterTypeApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS];
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+                int classifierIdx = m_classifierIdxApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
+                alfFiltering( m_classifier[classifierIdx], recBuf, buf, blkDst, blkSrc, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs, filterTypeCtb, m_fixFilterResult, fixedFilterSetIdx );
+#else
                 alfFiltering( m_classifier, recBuf, buf, blkDst, blkSrc, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs, filterTypeCtb, m_fixFilterResult, fixedFilterSetIdx );
+#endif
               }
 #else
               if( filterSetIndex >= NUM_FIXED_FILTER_SETS )
@@ -4458,7 +4828,12 @@ void EncAdaptiveLoopFilter::alfReconstructor(CodingStructure& cs, const PelUnitB
                 const Area blkDst(xStart >> chromaScaleX, yStart >> chromaScaleY, w >> chromaScaleX, h >> chromaScaleY);
                 const int alt_num = m_ctuAlternative[compID][ctuIdx];
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+                alfFiltering( m_classifier[0], recBuf, buf, blkDst, blkSrc, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_filterTypeApsChroma, nullptr, -1 );
+#else
+
                 alfFiltering( m_classifier, recBuf, buf, blkDst, blkSrc, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_filterTypeApsChroma, nullptr, -1 );
+#endif
 #else
                 m_filter5x5Blk( m_classifier, recBuf, buf, blkDst, blkSrc, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_alfVBChmaCTUHeight, m_alfVBChmaPos );
 #endif
@@ -4496,7 +4871,12 @@ void EncAdaptiveLoopFilter::alfReconstructor(CodingStructure& cs, const PelUnitB
           coeff = m_coeffApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
           clip = m_clippApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
           AlfFilterType filterTypeCtb = m_filterTypeApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS];
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          int classifierIdx = m_classifierIdxApsLuma[filterSetIndex - NUM_FIXED_FILTER_SETS][alt_num];
+          alfFiltering(m_classifier[classifierIdx], recBuf, recExtBuf, blk, blk, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs, filterTypeCtb, m_fixFilterResult, fixedFilterSetIdx);
+#else
           alfFiltering(m_classifier, recBuf, recExtBuf, blk, blk, COMPONENT_Y, coeff, clip, m_clpRngs.comp[COMPONENT_Y], cs, filterTypeCtb, m_fixFilterResult, fixedFilterSetIdx);
+#endif
         }
 #else
         if( filterSetIndex >= NUM_FIXED_FILTER_SETS )
@@ -4523,7 +4903,11 @@ void EncAdaptiveLoopFilter::alfReconstructor(CodingStructure& cs, const PelUnitB
           Area blk(xPos >> chromaScaleX, yPos >> chromaScaleY, width >> chromaScaleX, height >> chromaScaleY);
           const int alt_num = m_ctuAlternative[compID][ctuIdx];
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+          alfFiltering( m_classifier[0], recBuf, recExtBuf, blk, blk, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_filterTypeApsChroma, nullptr, -1 );
+#else
           alfFiltering( m_classifier, recBuf, recExtBuf, blk, blk, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_filterTypeApsChroma, nullptr, -1 );
+#endif
 #else
           m_filter5x5Blk( m_classifier, recBuf, recExtBuf, blk, blk, compID, m_chromaCoeffFinal[alt_num], m_chromaClippFinal[alt_num], m_clpRngs.comp[compIdx], cs, m_alfVBChmaCTUHeight, m_alfVBChmaPos );
 #endif
@@ -4619,8 +5003,14 @@ void EncAdaptiveLoopFilter::deriveCcAlfFilterCoeff( ComponentID compID, const Pe
     forward_tab[CCALF_CANDS_COEFF_NR - 1 + i] = CCALF_SMALL_TAB[i];
     forward_tab[CCALF_CANDS_COEFF_NR - 1 - i] = (-1) * CCALF_SMALL_TAB[i];
   }
+
+#if JVET_X0071_LONGER_CCALF
+  using TE = double[MAX_NUM_CC_ALF_CHROMA_COEFF][MAX_NUM_CC_ALF_CHROMA_COEFF];
+  using Ty = double[MAX_NUM_CC_ALF_CHROMA_COEFF];
+#else
   using TE = double[MAX_NUM_ALF_LUMA_COEFF][MAX_NUM_ALF_LUMA_COEFF];
   using Ty = double[MAX_NUM_ALF_LUMA_COEFF];
+#endif
 
   double filterCoeffDbl[MAX_NUM_CC_ALF_CHROMA_COEFF];
   int16_t filterCoeffInt[MAX_NUM_CC_ALF_CHROMA_COEFF];
@@ -4652,6 +5042,7 @@ void EncAdaptiveLoopFilter::deriveCcAlfFilterCoeff( ComponentID compID, const Pe
   // Refine quanitzation
   int modified       = 1;
   double errRef      = m_alfCovarianceFrameCcAlf[compID - 1][0][filterIdx].calcErrorForCcAlfCoeffs(filterCoeffInt, size, (m_scaleBits+1));
+
   while (modified)
   {
     modified = 0;
@@ -4986,7 +5377,11 @@ void EncAdaptiveLoopFilter::deriveCcAlfFilter( CodingStructure& cs, ComponentID
   bool   bestreuseTemporalFilterCoeff = false;
   std::vector<int> apsIds             = getAvailableCcAlfApsIds(cs, compID);
 
+#if JVET_X0071_LONGER_CCALF
+  for (int testFilterIdx = 0; testFilterIdx < (apsIds.size() + MAX_NUM_CC_ALF_FILTERS); testFilterIdx++)
+#else
   for (int testFilterIdx = 0; testFilterIdx < ( apsIds.size() + 1 ); testFilterIdx++ )
+#endif
   {
     bool referencingExistingAps   = (testFilterIdx < apsIds.size()) ? true : false;
     int maxNumberOfFiltersBeingTested = MAX_NUM_CC_ALF_FILTERS - (testFilterIdx - static_cast<int>(apsIds.size()));
@@ -5520,6 +5915,13 @@ void EncAdaptiveLoopFilter::calcCovarianceCcAlf(int ELocal[MAX_NUM_CC_ALF_CHROMA
   const Pel *recY0  = rec;
   const Pel *recYP1 = rec + 1 * stride;
   const Pel *recYP2 = rec + 2 * stride;
+#if JVET_X0071_LONGER_CCALF
+  const Pel *recYM2 = rec - 2 * stride;
+  const Pel *recYM3 = rec - 3 * stride;
+  const Pel *recYP3 = rec + 3 * stride;
+  const Pel *recYM4 = rec - 4 * stride;
+  const Pel *recYP4 = rec + 4 * stride;
+#endif
 
 #if !ALF_IMPROVEMENT
   if (vbDistance == -2 || vbDistance == +1)
@@ -5536,6 +5938,36 @@ void EncAdaptiveLoopFilter::calcCovarianceCcAlf(int ELocal[MAX_NUM_CC_ALF_CHROMA
   for (int b = 0; b < 1; b++)
   {
     const Pel centerValue = recY0[+0];
+
+#if JVET_X0071_LONGER_CCALF
+    ELocal[0][b] += recYM4[+0] - centerValue;
+    ELocal[1][b] += recYM3[+0] - centerValue;
+    ELocal[2][b] += recYM2[+0] - centerValue;
+    ELocal[3][b] += recYM1[+0] - centerValue;
+
+    ELocal[4][b] += recY0[-4] - centerValue;
+    ELocal[5][b] += recY0[-3] - centerValue;
+    ELocal[6][b] += recY0[-2] - centerValue;
+    ELocal[7][b] += recY0[-1] - centerValue;
+    ELocal[8][b] += recY0[+1] - centerValue;
+    ELocal[9][b] += recY0[+2] - centerValue;
+    ELocal[10][b] += recY0[+3] - centerValue;
+    ELocal[11][b] += recY0[+4] - centerValue;
+
+    ELocal[12][b] += recYP1[-4] - centerValue;
+    ELocal[13][b] += recYP1[-3] - centerValue;
+    ELocal[14][b] += recYP1[-2] - centerValue;
+    ELocal[15][b] += recYP1[-1] - centerValue;
+    ELocal[16][b] += recYP1[+0] - centerValue;
+    ELocal[17][b] += recYP1[+1] - centerValue;
+    ELocal[18][b] += recYP1[+2] - centerValue;
+    ELocal[19][b] += recYP1[+3] - centerValue;
+    ELocal[20][b] += recYP1[+4] - centerValue;
+
+    ELocal[21][b] += recYP2[+0] - centerValue;
+    ELocal[22][b] += recYP3[+0] - centerValue;
+    ELocal[23][b] += recYP4[+0] - centerValue;
+#else
     ELocal[0][b] += recYM1[+0] - centerValue;
     ELocal[1][b] += recY0[-1] - centerValue;
     ELocal[2][b] += recY0[+1] - centerValue;
@@ -5543,6 +5975,9 @@ void EncAdaptiveLoopFilter::calcCovarianceCcAlf(int ELocal[MAX_NUM_CC_ALF_CHROMA
     ELocal[4][b] += recYP1[+0] - centerValue;
     ELocal[5][b] += recYP1[+1] - centerValue;
     ELocal[6][b] += recYP2[+0] - centerValue;
+
+#endif
+
   }
 }
 
@@ -5550,10 +5985,14 @@ void EncAdaptiveLoopFilter::countLumaSwingGreaterThanThreshold(const Pel* luma,
 {
   const int lumaBitDepth = m_inputBitDepth[CH_L];
   const int threshold = (1 << ( m_inputBitDepth[CH_L] - 2 )) - 1;
-
+#if JVET_X0071_LONGER_CCALF
+  int xSupport[] = { 0,  0,  0,  0,  -4, -3,  -2, -1, 0, +1, +2, +3, +4,    -4,  -3,   -2,  -1, 0,   +1,  +2,  +3, +4,  0,  0, 0 };
+  int ySupport[] = { -4, -3, -2, -1,   0,  0,   0,  0, 0,  0,  0,  0, 0,    +1,  +1,   +1,  +1, +1,  +1,  +1,  +1, +1, +2, +3, +4 };
+#else
   // 3x4 Diamond
   int xSupport[] = {  0, -1, 0, 1, -1, 0, 1, 0 };
   int ySupport[] = { -1,  0, 0, 0,  1, 1, 1, 2 };
+#endif
 
   for (int y = 0; y < height; y += (1 << log2BlockHeight))
   {
@@ -5565,14 +6004,22 @@ void EncAdaptiveLoopFilter::countLumaSwingGreaterThanThreshold(const Pel* luma,
       {
         for (int xOff = 0; xOff < (1 << log2BlockWidth); xOff++)
         {
+#if JVET_X0071_LONGER_CCALF
+          if ((y + yOff) >= (height - 4) || (x + xOff) >= (width - 4) || (y + yOff) < 4 || (x + xOff) < 4) // only consider samples that are fully supported by picture
+#else
           if ((y + yOff) >= (height - 2) || (x + xOff) >= (width - 1) || (y + yOff) < 1 || (x + xOff) < 1) // only consider samples that are fully supported by picture
+#endif
           {
             continue;
           }
 
           int minVal = ((1 << lumaBitDepth) - 1);
           int maxVal = 0;
+#if JVET_X0071_LONGER_CCALF
+          for (int i = 0; i < MAX_NUM_CC_ALF_CHROMA_COEFF; i++)
+#else
           for (int i = 0; i < 8; i++)
+#endif
           {
             Pel p = luma[(yOff + ySupport[i]) * lumaStride + x + xOff + xSupport[i]];
 
diff --git a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h
index f970c20fb1ec0605774d793916f3c45772faebd2..7b738f6a4d7776c6994213884f1d1d33997250a8 100644
--- a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h
+++ b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h
@@ -47,8 +47,16 @@
 struct AlfCovariance
 {
   static constexpr int MaxAlfNumClippingValues = AdaptiveLoopFilter::MaxAlfNumClippingValues;
+
+#if JVET_X0071_LONGER_CCALF
+  using TE = double[MAX_NUM_CC_ALF_CHROMA_COEFF][MAX_NUM_CC_ALF_CHROMA_COEFF];
+  using Ty = double[MAX_NUM_CC_ALF_CHROMA_COEFF];
+#else
   using TE = double[MAX_NUM_ALF_LUMA_COEFF][MAX_NUM_ALF_LUMA_COEFF];
   using Ty = double[MAX_NUM_ALF_LUMA_COEFF];
+#endif
+
+
   using TKE = TE[AdaptiveLoopFilter::MaxAlfNumClippingValues][AdaptiveLoopFilter::MaxAlfNumClippingValues];
   using TKy = Ty[AdaptiveLoopFilter::MaxAlfNumClippingValues];
 
@@ -230,7 +238,11 @@ private:
   int                    m_alfWSSD;
   const EncCfg*          m_encCfg;
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  AlfCovariance*****       m_alfCovariance[MAX_NUM_COMPONENT];          // [compIdx][shapeIdx][ctbAddr][fixedFilterSetIdx][classifierInd][classIdx]
+#else
   AlfCovariance****       m_alfCovariance[MAX_NUM_COMPONENT];          // [compIdx][shapeIdx][ctbAddr][fixedFilterSetIdx][classIdx]
+#endif
 #else
   AlfCovariance***       m_alfCovariance[MAX_NUM_COMPONENT];          // [compIdx][shapeIdx][ctbAddr][classIdx]
 #endif
@@ -337,19 +349,27 @@ private:
                    );
 
   void   copyAlfParam( AlfParam& alfParamDst, AlfParam& alfParamSrc, ChannelType channel );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  double mergeFiltersAndCost( AlfParam& alfParam, AlfFilterShape& alfShape, AlfCovariance* covFrame, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], int& uiCoeffBits, int altIdx, int classifierIdx, int numFiltersLinear );
+  void   getFrameStats( ChannelType channel, int iShapeIdx, int altIdx, int fixedFilterSetIdx, int classifierIdx );
+#else
   double mergeFiltersAndCost( AlfParam& alfParam, AlfFilterShape& alfShape, AlfCovariance* covFrame, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], int& uiCoeffBits
 #if ALF_IMPROVEMENT
     , int altIdx
 #endif
   );
-
   void   getFrameStats( ChannelType channel, int iShapeIdx, int altIdx 
 #if ALF_IMPROVEMENT
     , int fixedFilterSetIdx
 #endif
   );
+#endif
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  void   getFrameStat( AlfCovariance* frameCov, AlfCovariance**** ctbCov, uint8_t* ctbEnableFlags, uint8_t* ctbAltIdx, const int numClasses, int altIdx, int fixedFilterSetIdx, int classifierIdx );
+#else
   void   getFrameStat( AlfCovariance* frameCov, AlfCovariance*** ctbCov, uint8_t* ctbEnableFlags, uint8_t* ctbAltIdx, const int numClasses, int altIdx, int fixedFilterSetIdx);
+#endif
 #else
   void   getFrameStat( AlfCovariance* frameCov, AlfCovariance** ctbCov, uint8_t* ctbEnableFlags, uint8_t* ctbAltIdx, const int numClasses, int altIdx );
 #endif
@@ -358,7 +378,11 @@ private:
 
 #if ALF_IMPROVEMENT
   template<bool m_alfWSSD>
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  void   getBlkStats(AlfCovariance* alfCovariace, const AlfFilterShape& shape, AlfClassifier** classifier, const Pel* org, const int orgStride, const Pel* rec, const int recStride, const CompArea& areaDst, const CompArea& area, const ChannelType channel, int fixedFilterSetIdx, int classifierIdx);
+#else
   void   getBlkStats(AlfCovariance* alfCovariace, const AlfFilterShape& shape, AlfClassifier** classifier, const Pel* org, const int orgStride, const Pel* rec, const int recStride, const CompArea& areaDst, const CompArea& area, const ChannelType channel, int fixedFilterSetIdx);
+#endif
 #if JVET_R0351_HIGH_BIT_DEPTH_SUPPORT
   void   calcCovariance(Pel ELocal[MAX_NUM_ALF_LUMA_COEFF][MaxAlfNumClippingValues], const Pel *rec, const int stride, const AlfFilterShape& shape, const int transposeIdx, const ChannelType channel, Pel ***fixedFitlerResults, Position pos, int fixedFilterSetIdx);
 #else  
@@ -393,24 +417,30 @@ private:
   void   calcCovarianceCcAlf(int ELocal[MAX_NUM_CC_ALF_CHROMA_COEFF][1], const Pel* rec, const int stride, const AlfFilterShape& shape, int vbDistance);
 #endif
 #endif
-
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+   void   mergeClasses(const AlfFilterShape& alfShape, AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], const int numClasses, short filterIndices[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES], const int altIdx, int mergedPair[MAX_NUM_ALF_CLASSES][2]);
+#else
   void   mergeClasses(const AlfFilterShape& alfShape, AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], const int numClasses, short filterIndices[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES]
 #if ALF_IMPROVEMENT
     , const int altIdx
 #endif
   );
-
+#endif
 
   double getFilterCoeffAndCost( CodingStructure& cs, double distUnfilter, ChannelType channel, bool bReCollectStat, int iShapeIdx, int& uiCoeffBits, 
 #if ALF_IMPROVEMENT
     int fixedFilterSetIdx,
 #endif
     bool onlyFilterCost = false );
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  double deriveFilterCoeffs(AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], AlfFilterShape& alfShape, short* filterIndices, int numFilters, double errorTabForce0Coeff[MAX_NUM_ALF_CLASSES][2], AlfParam& alfParam, bool nonLinear, int classifierIdx, bool isMaxNum, int mergedPair[MAX_NUM_ALF_CLASSES][2], int mergedCoeff[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], double mergedErr[MAX_NUM_ALF_CLASSES]);
+#else
   double deriveFilterCoeffs(AlfCovariance* cov, AlfCovariance* covMerged, int clipMerged[MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_CLASSES][MAX_NUM_ALF_LUMA_COEFF], AlfFilterShape& alfShape, short* filterIndices, int numFilters, double errorTabForce0Coeff[MAX_NUM_ALF_CLASSES][2], AlfParam& alfParam
 #if ALF_IMPROVEMENT
   , bool nonLinear
 #endif
   );
+#endif
   int    deriveFilterCoefficientsPredictionMode( AlfFilterShape& alfShape, int **filterSet, int** filterCoeffDiff, const int numFilters );
   double deriveCoeffQuant( int *filterClipp, int *filterCoeffQuant, const AlfCovariance& cov, const AlfFilterShape& shape, const int bitDepth, const bool optimizeClip );
   double deriveCtbAlfEnableFlags( CodingStructure& cs, const int iShapeIdx, ChannelType channel,
@@ -428,7 +458,11 @@ private:
   double getDistCoeffForce0( bool* codedVarBins, double errorForce0CoeffTab[MAX_NUM_ALF_CLASSES][2], int* bitsVarBin, int zeroBitsVarBin, const int numFilters);
   int    lengthUvlc( int uiCode );
 #if ALF_IMPROVEMENT
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+  int    getNonFilterCoeffRate( AlfParam& alfParam, int altIdx, int classifierIdx );
+#else
   int    getNonFilterCoeffRate( AlfParam& alfParam, int altIdx );
+#endif
   int    getCostFilterCoeffForce0( AlfFilterShape& alfShape, int **pDiffQFilterCoeffIntPP, const int numFilters, bool* codedVarBins, int altIdx);
   double getDistForce0( AlfFilterShape& alfShape, const int numFilters, double errorTabForce0Coeff[MAX_NUM_ALF_CLASSES][2], bool* codedVarBins, int altIdx);
   double getFilteredDistortion( AlfCovariance* cov, const int numClasses, const int numFiltersMinus1, const int numCoeff, int altIdx );
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 45863e593865c04182cbaff07b2d0c270f5a1cc6..d4d6ea185e2fb794c522d585d42529dfee112e2e 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -833,6 +833,11 @@ protected:
   int         m_BIFStrength;
   int         m_BIFQPOffset;
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  bool        m_CBIF;
+  int         m_CBIFStrength;
+  int         m_CBIFQPOffset;
+#endif
   
   bool        m_ccalf;
   int         m_ccalfQpThreshold;
@@ -1335,6 +1340,14 @@ public:
   void      setBIFQPOffset                  ( int val )       { m_BIFQPOffset = val; }
   int       getBIFQPOffset                  ()         const { return m_BIFQPOffset; }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  void      setUseCBIF                       ( bool b )       { m_CBIF = b; }
+  bool      getUseCBIF                       ()         const { return m_CBIF; }
+  void      setCBIFStrength                  ( int val )       { m_CBIFStrength = val; }
+  int       getCBIFStrength                  ()         const { return m_CBIFStrength; }
+  void      setCBIFQPOffset                  ( int val )       { m_CBIFQPOffset = val; }
+  int       getCBIFQPOffset                  ()         const { return m_CBIFQPOffset; }
+#endif
 #if MULTI_HYP_PRED
   void      setNumMHPCandsToTest(int i) { m_numMHPCandsToTest = i; }
   int       getNumMHPCandsToTest() { return m_numMHPCandsToTest; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 1dfb92b2de39984646ba50781dd1cf1cdbc9c997..4834044d527138d39bd8eca8056d81b9a05d0064 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -246,7 +246,7 @@ void EncCu::destroy()
   unsigned numWidths  = gp_sizeIdxInfo->numWidths();
   unsigned numHeights = gp_sizeIdxInfo->numHeights();
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   delete m_bilateralFilter;
 #endif
 
@@ -8478,6 +8478,52 @@ void EncCu::xCalDebCost( CodingStructure &cs, Partitioner &partitioner, bool cal
       }
     }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if(cs.pps->getUseCBIF()){
+        bool TU_VALID = false;
+        bool TU_CBF = false;
+        bool isDualTree = CS::isDualITree(cs);
+        bool chroma_valid = cu->Cb().valid() && cu->Cr().valid();
+        bool BIF_chroma = false;
+        for (auto &currTU : CU::traverseTUs(*cu))
+        {
+            bool isInter = (cu->predMode == MODE_INTER) ? true : false;
+
+            for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+            {
+                bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+
+                BIF_chroma = false;
+                if(!isDualTree && chroma_valid)
+                {
+                    TU_VALID = currTU.blocks[compIdx].valid();
+                    TU_CBF = false;
+                    if(TU_VALID)
+                    {
+                        TU_CBF = TU::getCbf(currTU, compID);
+                    }
+                    BIF_chroma = ((TU_CBF || isInter == false) && (currTU.cu->qp > 17) && (TU_VALID));
+                }
+
+                if(isDualTree && chroma_valid)
+                {
+                    TU_CBF = TU::getCbf(currTU, compID);
+                    BIF_chroma = ((TU_CBF || isInter == false) && (currTU.cu->qp > 17));
+                }
+
+                if (BIF_chroma)
+                {
+                    CompArea &compArea = currTU.block(compID);
+                    PelBuf    recBuf = picDbBuf.getBuf(compArea);
+                    PelBuf recIPredBuf = recBuf;
+
+                    m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(recBuf, recBuf, recBuf, currTU.cu->qp, recIPredBuf, cs.slice->clpRng(compID), currTU, true, isCb);
+                }
+            }
+        }
+    }
+#endif
     
     //deblock
     if ( leftEdgeAvai )
@@ -9103,6 +9149,59 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best
             }
           }
         }
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        const CompArea &area_chroma = cu.blocks[compID];
+        CompArea    tmpArea_chroma(compID, area_chroma.chromaFormat, Position(0, 0), area_chroma.size());
+        PelBuf tmpRecChroma;
+        if(isChroma(compID))
+        {
+            tmpRecChroma = m_tmpStorageLCU->getBuf(tmpArea_chroma);
+            tmpRecChroma.copyFrom(reco);
+        }
+
+        if(tempCS->pps->getUseCBIF() && isChroma(compID) && (cu.qp > 17))
+        {
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(*tempCS);
+            bool chroma_valid = cu.Cb().valid() && cu.Cr().valid();
+            bool BIF_chroma = false;
+
+            for (auto &currTU : CU::traverseTUs(cu))
+            {
+                Position tuPosInCu = currTU.chromaPos() - cu.chromaPos();
+                PelBuf tmpSubBuf = tmpRecChroma.subBuf(tuPosInCu, currTU.chromaSize());
+
+                bool isInter = (cu.predMode == MODE_INTER) ? true : false;
+                bool isCb = compID == COMPONENT_Cb ? true : false;
+                BIF_chroma = false;
+                if(!isDualTree && chroma_valid)
+                {
+                    TU_VALID = currTU.blocks[compID].valid();
+                    TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+
+                    if(TU_VALID)
+                    {
+                        TU_CBF = TU::getCbf(currTU, compID);
+                    }
+                    BIF_chroma = (( TU_CBF || isInter == false) && (currTU.cu->qp > 17) && (TU_VALID));
+
+                }
+
+                if(isDualTree && chroma_valid)
+                {
+                    BIF_chroma = ((TU::getCbf(currTU, compID) || isInter == false) && (currTU.cu->qp > 17));
+                }
+
+                if(BIF_chroma)
+                {
+                    CompArea compArea = currTU.blocks[compID];
+                    PelBuf recIPredBuf = tempCS->slice->getPic()->getRecoBuf(compArea);
+                    m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpSubBuf, tmpSubBuf, tmpSubBuf, currTU.cu->qp, recIPredBuf, tempCS->slice->clpRng(compID), currTU, true, isCb);
+                }
+            }
+        }
+#endif
 
 #if WCG_EXT
         if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (
@@ -9115,7 +9214,18 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best
           }
           else
           {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+            if(isChroma(compID) && tempCS->pps->getUseCBIF())
+            {
+                finalDistortion += m_pcRdCost->getDistPart(org, tmpRecChroma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
+            }
+            else
+            {
+                finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE_WTD, &orgLuma );
+            }
+#else
             finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE_WTD, &orgLuma );
+#endif
           }
         }
         else
@@ -9124,6 +9234,56 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best
           finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE );
         }
 #else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        const CompArea &area_chroma = cu.blocks[compID];
+        CompArea    tmpArea_chroma(compID, area_chroma.chromaFormat, Position(0, 0), area_chroma.size());
+        PelBuf tmpRecChroma;
+        if(isChroma(compID))
+        {
+            tmpRecChroma = m_tmpStorageLCU->getBuf(tmpArea_chroma);
+            tmpRecChroma.copyFrom(reco);
+        }
+
+        if(tempCS->pps->getUseCBIF() && isChroma(compID) && (cu.qp > 17))
+        {
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(*tempCS);
+            bool chroma_valid = cu.Cb().valid() && cu.Cr().valid();
+            bool BIF_chroma = false;
+
+            for (auto &currTU : CU::traverseTUs(cu))
+            {
+                Position tuPosInCu = currTU.chromaPos() - cu.chromaPos();
+                PelBuf tmpSubBuf = tmpRecChroma.subBuf(tuPosInCu, currTU.chromaSize());
+
+                bool isInter = (cu.predMode == MODE_INTER) ? true : false;
+                bool isCb = compID == COMPONENT_Cb ? true : false;
+                BIF_chroma = false;
+                if(!isDualTree && chroma_valid)
+                {
+                    TU_VALID = currTU.blocks[compID].valid();
+                    TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+
+                    if(TU_VALID){
+                        TU_CBF = TU::getCbf(currTU, compID);
+                    }
+                    BIF_chroma = (( TU_CBF || isInter == false) && (currTU.cu->qp > 17) && (TU_VALID));
+                }
+
+                if(isDualTree && chroma_valid){
+                    BIF_chroma = ((TU::getCbf(currTU, compID) || isInter == false) && (currTU.cu->qp > 17));
+                }
+
+                if(BIF_chroma)
+                {
+                    CompArea compArea = currTU.blocks[compID];
+                    PelBuf recIPredBuf = tempCS->slice->getPic()->getRecoBuf(compArea);
+                    m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpSubBuf, tmpSubBuf, tmpSubBuf, currTU.cu->qp, recIPredBuf, tempCS->slice->clpRng(compID), currTU, true, isCb);
+                }
+            }
+        }
+#endif
 #if WCG_EXT
       if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcEncCfg->getLmcs() && (tempCS->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())))
       {
@@ -9138,7 +9298,18 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best
         }
         else
         {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+          if(isChroma(compID) && tempCS->pps->getUseCBIF())
+          {
+            finalDistortion += m_pcRdCost->getDistPart(org, tmpRecChroma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
+          }
+          else
+          {
+            finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE_WTD, &orgLuma );
+          }
+#else
           finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE_WTD, &orgLuma );
+#endif
         }
       }
       else
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index 2bef92f6e6020e046679d690d3cae26ead9b0580..649480272fef1e2b3cc0eb2ac292f8eeb9a95a04 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -46,7 +46,7 @@
 #include "CommonLib/Unit.h"
 #include "CommonLib/UnitPartitioner.h"
 #include "CommonLib/IbcHashMap.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "CommonLib/BilateralFilter.h"
 #endif
 #include "CommonLib/LoopFilter.h"
@@ -334,7 +334,7 @@ public:
 
   EncModeCtrl* getModeCtrl  () { return m_modeCtrl; }
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter *m_bilateralFilter = new BilateralFilter();
 #endif
 
diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp
index f89642786fd3509d4005e4cf48d5dd2dcb549838..c3494b17662062e06f56b55494ff4acc9561e8af 100644
--- a/source/Lib/EncoderLib/EncGOP.cpp
+++ b/source/Lib/EncoderLib/EncGOP.cpp
@@ -178,7 +178,7 @@ void  EncGOP::create()
 {
   m_bLongtermTestPictureHasBeenCoded = 0;
   m_bLongtermTestPictureHasBeenCoded2 = 0;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_cBilateralFilter.create();
 #endif
 }
@@ -205,7 +205,7 @@ void  EncGOP::destroy()
     delete m_picOrig;
     m_picOrig = NULL;
   }
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_cBilateralFilter.destroy();
 #endif
 }
@@ -1968,7 +1968,31 @@ public:
   }
 };
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+class CBIFCabacEstImp : public CBIFCabacEst
+{
+    CABACWriter* CABACEstimator;
+public:
+    CBIFCabacEstImp(CABACWriter* _CABACEstimator) : CABACEstimator(_CABACEstimator) {};
+    virtual ~CBIFCabacEstImp() {};
+
+    virtual uint64_t getBits_Cb(const Slice& slice, const CBifParams& htdfParams)
+    {
+        CABACEstimator->initCtxModels(slice);
+        CABACEstimator->resetBits();
+        CABACEstimator->Cbif_Cb(slice, htdfParams);
+        return CABACEstimator->getEstFracBits();
+    }
 
+    virtual uint64_t getBits_Cr(const Slice& slice, const CBifParams& htdfParams)
+    {
+        CABACEstimator->initCtxModels(slice);
+        CABACEstimator->resetBits();
+        CABACEstimator->Cbif_Cr(slice, htdfParams);
+        return CABACEstimator->getEstFracBits();
+    }
+};
+#endif
 
 // ====================================================================================================================
 // Public member functions
@@ -2786,11 +2810,19 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
 #endif
 #endif
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if (pcSlice->getSPS()->getSAOEnabledFlag() || pcSlice->getPPS()->getUseBIF() || pcSlice->getPPS()->getUseCBIF())
+#else
     // BIF happens in SAO code so this needs to be done
     // even if SAO=0 if BIF=1.
     if (pcSlice->getSPS()->getSAOEnabledFlag() || pcSlice->getPPS()->getUseBIF() )
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if (pcSlice->getSPS()->getSAOEnabledFlag() || pcSlice->getPPS()->getUseCBIF())
 #else
     if (pcSlice->getSPS()->getSAOEnabledFlag())
+#endif
 #endif
     {
       pcPic->resizeSAO( numberOfCtusInFrame, 0 );
@@ -3131,10 +3163,18 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
 #endif
 
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if( pcSlice->getSPS()->getSAOEnabledFlag() || pcSlice->getPPS()->getUseBIF() || pcSlice->getPPS()->getUseCBIF())
+#else
       // We need to do this step if at least one of BIF or SAO are enabled.
       if( pcSlice->getSPS()->getSAOEnabledFlag() || pcSlice->getPPS()->getUseBIF())
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if( pcSlice->getSPS()->getSAOEnabledFlag() || pcSlice->getPPS()->getUseCBIF())
 #else
       if( pcSlice->getSPS()->getSAOEnabledFlag() )
+#endif
 #endif
       {
         bool sliceEnabled[MAX_NUM_COMPONENT];
@@ -3142,6 +3182,9 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
 #if JVET_V0094_BILATERAL_FILTER
         BIFCabacEstImp est(m_pcEncLib->getCABACEncoder()->getCABACEstimator(cs.slice->getSPS()));
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        CBIFCabacEstImp CBIF_est(m_pcEncLib->getCABACEncoder()->getCABACEstimator(cs.slice->getSPS()));
+#endif
         
         m_pcSAO->SAOProcess( cs, sliceEnabled, pcSlice->getLambdas(),
 #if ENABLE_QPA
@@ -3150,10 +3193,13 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
                              m_pcCfg->getTestSAODisableAtPictureLevel(), m_pcCfg->getSaoEncodingRate(), m_pcCfg->getSaoEncodingRateChroma(), m_pcCfg->getSaoCtuBoundary(), m_pcCfg->getSaoGreedyMergeEnc()
 #if JVET_V0094_BILATERAL_FILTER
                               , &est
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+                              , &CBIF_est
 #endif
                             );
         //assign SAO slice header
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
         if( pcSlice->getSPS()->getSAOEnabledFlag() )
         {
 #endif
@@ -3174,7 +3220,7 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic,
         }
 
         }
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
       }
 #endif
       }
diff --git a/source/Lib/EncoderLib/EncGOP.h b/source/Lib/EncoderLib/EncGOP.h
index be9bbec3a72df5e941d74b6e0cf080dca9227fb5..c25ffdcd8ddbb53487a467cc434e82b756cc6d24 100644
--- a/source/Lib/EncoderLib/EncGOP.h
+++ b/source/Lib/EncoderLib/EncGOP.h
@@ -60,7 +60,7 @@
 #include "Analyze.h"
 #include "RateCtrl.h"
 #include <vector>
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "BilateralFilter.h"
 #endif
 #include "EncHRD.h"
@@ -140,7 +140,7 @@ private:
   PicList*                m_pcListPic;
 
   HLSWriter*              m_HLSWriter;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter         m_cBilateralFilter;
 #endif
   LoopFilter*             m_pcLoopFilter;
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 5060c6595f5cf9b7058967b82b2ddc84bda9317d..b40776947fb785a9c553814d73f13cd17396f81a 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -111,7 +111,7 @@ void EncLib::create( const int layerId )
   m_cCuEncoder      = new EncCu              [m_numCuEncStacks];
   m_cInterSearch    = new InterSearch        [m_numCuEncStacks];
   m_cIntraSearch    = new IntraSearch        [m_numCuEncStacks];
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_bilateralFilter = new BilateralFilter    [m_numCuEncStacks];
 #endif
   m_cTrQuant        = new TrQuant            [m_numCuEncStacks];
@@ -122,14 +122,14 @@ void EncLib::create( const int layerId )
   for( int jId = 0; jId < m_numCuEncStacks; jId++ )
   {
     m_cCuEncoder[jId].         create( this );
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
     m_bilateralFilter[jId].    create();
 #endif
 
   }
 #else
   m_cCuEncoder.         create( this );
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_bilateralFilter.    create();
 #endif
 #endif
@@ -170,16 +170,32 @@ void EncLib::create( const int layerId )
   }
 #if JVET_V0094_BILATERAL_FILTER
 #if JVET_W0066_CCSAO
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if (m_bUseSAO || m_BIF || m_CCSAO || m_CBIF)
+#else
   if (m_bUseSAO || m_BIF || m_CCSAO)
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if (m_bUseSAO || m_BIF || m_CBIF)
 #else
   if (m_bUseSAO || m_BIF)
 #endif
+#endif
 #else
 #if JVET_W0066_CCSAO
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if (m_bUseSAO || m_CCSAO || m_CBIF)
+#else
   if (m_bUseSAO || m_CCSAO)
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if (m_bUseSAO || m_CBIF)
 #else
   if (m_bUseSAO)
 #endif
+#endif
 #endif
   {
     const uint32_t widthInCtus = (m_iSourceWidth + m_maxCUWidth - 1) / m_maxCUWidth;
@@ -224,14 +240,14 @@ void EncLib::destroy ()
   {
     m_cInterSearch[jId].   destroy();
     m_cIntraSearch[jId].   destroy();
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
     m_bilateralFilter[jId].destroy();
 #endif
   }
 #else
   m_cInterSearch.       destroy();
   m_cIntraSearch.       destroy();
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_bilateralFilter.    destroy();
 #endif
 #endif
@@ -240,7 +256,7 @@ void EncLib::destroy ()
   delete[] m_cCuEncoder;
   delete[] m_cInterSearch;
   delete[] m_cIntraSearch;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   delete[] m_bilateralFilter;
 #endif
   delete[] m_cTrQuant;
@@ -444,7 +460,7 @@ void EncLib::init( bool isFieldCoding, AUWriterIf* auWriterIf )
     // initialize encoder search class
     CABACWriter* cabacEstimator = m_CABACEncoder[jId].getCABACEstimator( &sps0 );
     m_cIntraSearch[jId].init( this,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                              &m_bilateralFilter[jId],
 #endif
                               &m_cTrQuant[jId],
@@ -455,7 +471,7 @@ void EncLib::init( bool isFieldCoding, AUWriterIf* auWriterIf )
                             , sps0.getBitDepth(CHANNEL_TYPE_LUMA)
     );
     m_cInterSearch[jId].init( this,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                              &m_bilateralFilter[jId],
 #endif
                               &m_cTrQuant[jId],
@@ -487,7 +503,7 @@ void EncLib::init( bool isFieldCoding, AUWriterIf* auWriterIf )
   // initialize encoder search class
   CABACWriter* cabacEstimator = m_CABACEncoder.getCABACEstimator(&sps0);
   m_cIntraSearch.init( this,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                        &m_bilateralFilter,
 #endif
                        &m_cTrQuant,
@@ -498,7 +514,7 @@ void EncLib::init( bool isFieldCoding, AUWriterIf* auWriterIf )
                      , sps0.getBitDepth(CHANNEL_TYPE_LUMA)
   );
   m_cInterSearch.init( this,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                        &m_bilateralFilter,
 #endif
                        &m_cTrQuant,
@@ -1926,6 +1942,11 @@ void EncLib::xInitPPS(PPS &pps, const SPS &sps)
   pps.setBIFStrength           ( m_BIFStrength );
   pps.setBIFQPOffset           ( m_BIFQPOffset );
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  pps.setUseCBIF                ( m_CBIF );
+  pps.setCBIFStrength           ( m_CBIFStrength );
+  pps.setCBIFQPOffset           ( m_CBIFQPOffset );
+#endif
 
   if ( getDeblockingFilterMetric() )
   {
diff --git a/source/Lib/EncoderLib/EncLib.h b/source/Lib/EncoderLib/EncLib.h
index f05898fa7d53d7b92c5f5f84b6b9d92497e373fd..25e7b28dd1812e4e5a2fd3f840ade317e33d6669 100644
--- a/source/Lib/EncoderLib/EncLib.h
+++ b/source/Lib/EncoderLib/EncLib.h
@@ -42,7 +42,7 @@
 #include "CommonLib/TrQuant.h"
 #include "CommonLib/LoopFilter.h"
 #include "CommonLib/NAL.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "CommonLib/BilateralFilter.h"
 #endif
 
@@ -91,12 +91,12 @@ private:
 #endif
   // coding tool
 #if ENABLE_SPLIT_PARALLELISM
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter          *m_bilateralFilter;
 #endif
   TrQuant                  *m_cTrQuant;                           ///< transform & quantization class
 #else
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter           m_bilateralFilter;
 #endif
   TrQuant                   m_cTrQuant;                           ///< transform & quantization class
@@ -205,7 +205,7 @@ public:
   InterSearch*            getInterSearch        ( int jId = 0 ) { return  &m_cInterSearch[jId];    }
   IntraSearch*            getIntraSearch        ( int jId = 0 ) { return  &m_cIntraSearch[jId];    }
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter*        getBilateralFilter    ( int jId = 0 ) { return  &m_bilateralFilter[jId]; }
 #endif
   TrQuant*                getTrQuant            ( int jId = 0 ) { return  &m_cTrQuant[jId];        }
@@ -213,7 +213,7 @@ public:
   InterSearch*            getInterSearch        ()              { return  &m_cInterSearch;         }
   IntraSearch*            getIntraSearch        ()              { return  &m_cIntraSearch;         }
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter*        getBilateralFilter    ()              { return  &m_bilateralFilter;      }
 #endif
   TrQuant*                getTrQuant            ()              { return  &m_cTrQuant;             }
diff --git a/source/Lib/EncoderLib/EncSampleAdaptiveOffset.cpp b/source/Lib/EncoderLib/EncSampleAdaptiveOffset.cpp
index 019e5561be67111763b9440e06f3542073aa8bb0..5a5df72140281ad9e599b1b525e5a4d971cbebb8 100644
--- a/source/Lib/EncoderLib/EncSampleAdaptiveOffset.cpp
+++ b/source/Lib/EncoderLib/EncSampleAdaptiveOffset.cpp
@@ -41,7 +41,7 @@
 #include "CommonLib/dtrace_codingstruct.h"
 #include "CommonLib/dtrace_buffer.h"
 #include "CommonLib/CodingStructure.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "CommonLib/BilateralFilter.h"
 #endif
 
@@ -280,17 +280,20 @@ void EncSampleAdaptiveOffset::SAOProcess( CodingStructure& cs, bool* sliceEnable
                                           const bool bTestSAODisableAtPictureLevel, const double saoEncodingRate, const double saoEncodingRateChroma, const bool isPreDBFSamplesUsed, bool isGreedyMergeEncoding
 #if JVET_V0094_BILATERAL_FILTER
                                           ,BIFCabacEst* BifCABACEstimator
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+                                          ,CBIFCabacEst* CBifCABACEstimator
 #endif
                                          )
 {
-#if ALF_SAO_TRUE_ORG && !JVET_V0094_BILATERAL_FILTER
+#if ALF_SAO_TRUE_ORG && !JVET_V0094_BILATERAL_FILTER && !JVET_X0071_CHROMA_BILATERAL_FILTER
   PelUnitBuf org = cs.getTrueOrgBuf();
 #else
   PelUnitBuf org = cs.getOrgBuf();
 #endif
   PelUnitBuf res = cs.getRecoBuf();
   PelUnitBuf src = m_tempBuf;
-#if !JVET_V0094_BILATERAL_FILTER
+#if !JVET_V0094_BILATERAL_FILTER && !JVET_X0071_CHROMA_BILATERAL_FILTER
   // Moved until after the bilateral filter has been initialized
   memcpy(m_lambda, lambdas, sizeof(m_lambda));
 #endif
@@ -327,8 +330,71 @@ void EncSampleAdaptiveOffset::SAOProcess( CodingStructure& cs, bool* sliceEnable
   //double MseFltDefSwitchFrame = 0;
   //int CtuIdx = 0;
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if(cs.pps->getUseCBIF()){
+    const PreCalcValues& pcv = *cs.pcv;
+    CBifParams& CBifParams = cs.picture->getCBifParam();
+    int width = cs.picture->lwidth();
+    int height = cs.picture->lheight();
+    int block_width = pcv.maxCUWidth;
+    int block_height = pcv.maxCUHeight;
+
+    int width_in_blocks = width / block_width + (width % block_width != 0);
+    int height_in_blocks = height / block_height + (height % block_height != 0);
+
+    CBifParams.numBlocks = width_in_blocks * height_in_blocks;
+
+    CBifParams.ctuOn_Cb.resize(CBifParams.numBlocks);
+    CBifParams.ctuOn_Cr.resize(CBifParams.numBlocks);
+    std::fill(CBifParams.ctuOn_Cb.begin(), CBifParams.ctuOn_Cb.end(), 0);
+    std::fill(CBifParams.ctuOn_Cr.begin(), CBifParams.ctuOn_Cr.end(), 0);
+    CBifParams.frmOn_Cb = 0;
+    CBifParams.frmOn_Cr = 0;
+    CBifParams.allCtuOn_Cb = 0;
+    CBifParams.allCtuOn_Cr = 0;
+
+    if (CBifParams.frmOn_Cb == 0)
+    {
+        std::fill(CBifParams.ctuOn_Cb.begin(), CBifParams.ctuOn_Cb.end(), 0);
+    }
+    else if (CBifParams.allCtuOn_Cb)
+    {
+        std::fill(CBifParams.ctuOn_Cb.begin(), CBifParams.ctuOn_Cb.end(), 1);
+    }
+
+    if (CBifParams.frmOn_Cr == 0)
+    {
+        std::fill(CBifParams.ctuOn_Cr.begin(), CBifParams.ctuOn_Cr.end(), 0);
+    }
+    else if (CBifParams.allCtuOn_Cr)
+    {
+        std::fill(CBifParams.ctuOn_Cr.begin(), CBifParams.ctuOn_Cr.end(), 1);
+    }
+  }
+#endif
   
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    BilateralFilter bilateralFilter;
+    if(!cs.sps->getSAOEnabledFlag() && (cs.pps->getUseBIF() || cs.pps->getUseCBIF()))
+    {
+        bilateralFilter.create();
+        if(cs.pps->getUseBIF())
+        {
+            bilateralFilter.bilateralFilterPicRDOperCTU(cs, src, BifCABACEstimator); // Filters from src to res
+        }
+        if(cs.pps->getUseCBIF())
+        {
+            //Cb
+            bilateralFilter.bilateralFilterPicRDOperCTU_chroma(cs, src, CBifCABACEstimator, true);
+            //Cr
+            bilateralFilter.bilateralFilterPicRDOperCTU_chroma(cs, src, CBifCABACEstimator, false);
+        }
+        bilateralFilter.destroy();
+        return;
+    }
+    memcpy(m_lambda, lambdas, sizeof(m_lambda));
+#else
   BilateralFilter bilateralFilter;
   
   // Special case when SAO = 0 and BIF = 1.
@@ -342,10 +408,51 @@ void EncSampleAdaptiveOffset::SAOProcess( CodingStructure& cs, bool* sliceEnable
     return;
   }
   memcpy(m_lambda, lambdas, sizeof(m_lambda));
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    BilateralFilter bilateralFilter;
+    if(!cs.sps->getSAOEnabledFlag() && cs.pps->getUseCBIF())
+    {
+        bilateralFilter.create();
+        //Cb
+        bilateralFilter.bilateralFilterPicRDOperCTU_chroma(cs, src, CBifCABACEstimator, true);
+        //Cr
+        bilateralFilter.bilateralFilterPicRDOperCTU_chroma(cs, src, CBifCABACEstimator, false);
+        bilateralFilter.destroy();
+        return;
+    }
+    memcpy(m_lambda, lambdas, sizeof(m_lambda));
+#else
+    //do nothing
+#endif
 #endif
   
   //collect statistics
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if(cs.pps->getUseBIF() || cs.pps->getUseCBIF())
+  {
+    bilateralFilter.create();
+    if(cs.pps->getUseBIF())
+    {
+        bilateralFilter.bilateralFilterPicRDOperCTU(cs, src, BifCABACEstimator); // Filters from src to res'
+    }
+    if(cs.pps->getUseCBIF())
+    {
+        //Cb
+        bilateralFilter.bilateralFilterPicRDOperCTU_chroma(cs, src, CBifCABACEstimator, true);
+        //Cr
+        bilateralFilter.bilateralFilterPicRDOperCTU_chroma(cs, src, CBifCABACEstimator, false);
+    }
+    getStatistics(m_statData, org, src, res, cs);
+    bilateralFilter.destroy();
+  }
+  else
+  {
+    getStatistics(m_statData, org, src, src, cs);
+  }
+#else
   //apply BILAT to res
   if(cs.pps->getUseBIF())
   {
@@ -358,8 +465,26 @@ void EncSampleAdaptiveOffset::SAOProcess( CodingStructure& cs, bool* sliceEnable
   {
     getStatistics(m_statData, org, src, src, cs);
   }
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if(cs.pps->getUseCBIF())
+  {
+    bilateralFilter.create();
+    //Cb
+    bilateralFilter.bilateralFilterPicRDOperCTU_chroma(cs, src, CBifCABACEstimator, true);
+    //Cr
+    bilateralFilter.bilateralFilterPicRDOperCTU_chroma(cs, src, CBifCABACEstimator, false);
+    getStatistics(m_statData, org, src, res, cs);
+    bilateralFilter.destroy();
+  }
+  else
+  {
+    getStatistics(m_statData, org, src, src, cs);
+  }
 #else
   getStatistics(m_statData, org, src, cs);
+#endif
 #endif
 
   if(isPreDBFSamplesUsed)
@@ -368,9 +493,25 @@ void EncSampleAdaptiveOffset::SAOProcess( CodingStructure& cs, bool* sliceEnable
   }
   
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if(cs.pps->getUseBIF() || cs.pps->getUseCBIF())
+  {
+    res.copyFrom(src);
+  }
+#else
   //undo BILAT on res
   if(cs.pps->getUseBIF())
     res.copyFrom(src);
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if(cs.pps->getUseCBIF())
+  {
+    res.copyFrom(src);
+  }
+#else
+    //do nothing
+#endif
 #endif
   
   //slice on/off
@@ -404,7 +545,7 @@ void EncSampleAdaptiveOffset::getPreDBFStatistics(CodingStructure& cs)
 #endif
   PelUnitBuf rec = cs.getRecoBuf();
   getStatistics(m_preDBFstatData, org,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                 rec, rec,
 #else
                 rec,
@@ -429,7 +570,7 @@ void EncSampleAdaptiveOffset::addPreDBFStatistics(std::vector<SAOStatData**>& bl
 }
 
 void EncSampleAdaptiveOffset::getStatistics(std::vector<SAOStatData**>& blkStats, PelUnitBuf& orgYuv, PelUnitBuf& srcYuv,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                                             PelUnitBuf& bifYuv,
 #endif
                                             CodingStructure& cs, bool isCalculatePreDeblockSamples)
@@ -482,7 +623,7 @@ void EncSampleAdaptiveOffset::getStatistics(std::vector<SAOStatData**>& blkStats
         int  orgStride  = orgYuv.get(compID).stride;
         Pel* orgBlk     = orgYuv.get(compID).bufAt( compArea );
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
         int  bifStride  = bifYuv.get(compID).stride;
         Pel* bifBlk     = bifYuv.get(compID).bufAt( compArea );
 #endif
@@ -498,7 +639,7 @@ void EncSampleAdaptiveOffset::getStatistics(std::vector<SAOStatData**>& blkStats
 
         getBlkStats(compID, cs.sps->getBitDepth(toChannelType(compID)), blkStats[ctuRsAddr][compID]
                   , srcBlk, orgBlk,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                     bifBlk, bifStride,
 #endif
                     srcStride, orgStride, compArea.width, compArea.height
@@ -960,7 +1101,7 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
 {
   const PreCalcValues& pcv = *cs.pcv;
   bool allBlksDisabled = true;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   if(cs.sps->getSAOEnabledFlag())
   {
     // If SAO is enabled, we should investigate the components.
@@ -974,11 +1115,11 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
       allBlksDisabled = false;
     }
   }
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   }
 #endif
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter bilateralFilter;
   bilateralFilter.create();
 #endif
@@ -1032,6 +1173,12 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
       {
         codedParams[ctuRsAddr].reset();
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(!cs.pps->getUseBIF() && !cs.pps->getUseCBIF())
+        {
+            continue;
+        }
+#else
         // In the combined filter, we cannot continue here, even if SAO is
         // turned off for all blocks, since we need to perform the bilateral
         // filter later on (see next JVET_V0094_BILATERAL_FILTER).
@@ -1040,8 +1187,16 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
           // Unless we are not using BIF. Then it is safe to continue.
           continue;
         }
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(!cs.pps->getUseCBIF())
+        {
+            continue;
+        }
 #else
         continue;
+#endif
 #endif
       }
 
@@ -1246,9 +1401,64 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
         }
       }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if(cs.pps->getUseCBIF())
+      {
+        CBifParams& CBifParams = cs.picture->getCBifParam();
+
+        bool TU_VALID = false;
+        bool TU_CBF = false;
+        bool isDualTree = CS::isDualITree(cs);
+        ChannelType CType = isDualTree ? CH_C : CH_L;
+        bool BIF_chroma = false;
+
+        for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+        {
+            bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+            if(!chroma_valid){
+                continue;
+            }
+            for (auto &currTU : CU::traverseTUs(currCU))
+            {
+                bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+                for(int compIdx = COMPONENT_Cb ; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                {
+                    bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                    ComponentID compID = isCb ? COMPONENT_Cb :COMPONENT_Cr;
+                    bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                    BIF_chroma = false;
+                    if(!isDualTree)
+                    {
+                        TU_VALID = currTU.blocks[compIdx].valid();
+                        TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+                        if(TU_VALID)
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                        }
+                        BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                    }
+                    else
+                    {
+                        TU_CBF = TU::getCbf(currTU, compID);
+                        BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                    }
+                    if(BIF_chroma)
+                    {
+                        bilateralFilter.bilateralFilterDiamond5x5NoClip_chroma(srcYuv, resYuv, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                    }
+                }
+            }
+        }
+    }
+#endif
 #else
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(cs.pps->getUseBIF() || cs.pps->getUseCBIF())
+#else
         if(cs.pps->getUseBIF())
+#endif
         {
           offsetCTUnoClip(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
           // Avoid old slow code
@@ -1263,6 +1473,25 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
           bool clipLumaIfNoBilat = false;
           if(myCtbOffset.modeIdc != SAO_MODE_OFF)
             clipLumaIfNoBilat = true;
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+          SAOOffset& myCtbOffset_Cb     = mySAOblkParam[1];
+          SAOOffset& myCtbOffset_Cr     = mySAOblkParam[2];
+          CBifParams& CBifParams = cs.picture->getCBifParam();
+
+          bool clipChromaIfNoBilat_Cb = false;
+          bool clipChromaIfNoBilat_Cr = false;
+
+          if(myCtbOffset_Cb.modeIdc != SAO_MODE_OFF)
+          {
+              clipChromaIfNoBilat_Cb = true;
+          }
+          if(myCtbOffset_Cr.modeIdc != SAO_MODE_OFF)
+          {
+              clipChromaIfNoBilat_Cr = true;
+          }
+          if(cs.pps->getUseBIF())
+          {
+#endif
           
           for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
           {
@@ -1282,15 +1511,215 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
               }
             }
           }
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+          } // BIF LUMA is disabled
+          else{
+              for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
+              {
+                  for (auto &currTU : CU::traverseTUs(currCU))
+                  {
+                      if(clipLumaIfNoBilat)
+                      {
+                          m_bilateralFilter.clipNotBilaterallyFilteredBlocks(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Y), currTU);
+                      }
+                  }
+              }
+          }
+          if(cs.pps->getUseCBIF())
+          {
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+            bool BIF_chroma = false;
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+
+                    for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                    {
+                        bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                        ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+                        bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                        BIF_chroma = false;
+                        if(!isDualTree)
+                        {
+                            TU_VALID = currTU.blocks[compIdx].valid();
+                            TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+
+                            if(TU_VALID)
+                            {
+                                TU_CBF = TU::getCbf(currTU, compID);
+                            }
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                        }
+                        else
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                        }
+                        if(BIF_chroma)
+                        {
+                            m_bilateralFilter.bilateralFilterDiamond5x5_chroma(srcYuv, resYuv, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                        }
+                        else
+                        {
+                            bool use_clip = isCb ? clipChromaIfNoBilat_Cb : clipChromaIfNoBilat_Cr;
+                            if(use_clip && currTU.blocks[compID].valid())
+                            {
+                                m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(srcYuv, resYuv, cs.slice->clpRng(compID), currTU, isCb);
+                            }
+                        }
+                    }
+                 }
+              }
+          }// BIF chroma is disabled
+          else
+          {
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    if(clipChromaIfNoBilat_Cb && currTU.blocks[COMPONENT_Cb].valid())
+                    {
+                        m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Cb), currTU, true);
+                    }
+                    if(clipChromaIfNoBilat_Cr && currTU.blocks[COMPONENT_Cr].valid())
+                    {
+                        m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Cr), currTU, false);
+                    }
+                }
+            }
+        }
+#endif
         }
         else
         {
           // We do not do BIF for this sequence, so we can use the regular SAO function
           offsetCTU(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
         }
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+       if(cs.pps->getUseCBIF())
+       {
+            offsetCTUnoClip(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
+
+            SAOBlkParam mySAOblkParam = cs.picture->getSAO()[ctuRsAddr];
+            SAOOffset& myCtbOffset     = mySAOblkParam[0];
+
+            bool clipLumaIfNoBilat = false;
+            if(myCtbOffset.modeIdc != SAO_MODE_OFF)
+            {
+                clipLumaIfNoBilat = true;
+            }
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
+            {
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    if(clipLumaIfNoBilat)
+                        bilateralFilter.clipNotBilaterallyFilteredBlocks(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Y), currTU);
+                }
+            }
+
+            SAOOffset& myCtbOffset_Cb     = mySAOblkParam[1];
+            SAOOffset& myCtbOffset_Cr     = mySAOblkParam[2];
+            bool clipChromaIfNoBilat_Cb = false;
+            bool clipChromaIfNoBilat_Cr = false;
+            CBifParams& CBifParams = cs.picture->getCBifParam();
+
+            if(myCtbOffset_Cb.modeIdc != SAO_MODE_OFF)
+            {
+                clipChromaIfNoBilat_Cb = true;
+            }
+            if(myCtbOffset_Cr.modeIdc != SAO_MODE_OFF)
+            {
+                clipChromaIfNoBilat_Cr = true;
+            }
+
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+            bool BIF_chroma = false;
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+
+                    for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                    {
+                        bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                        ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+                        bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                        BIF_chroma = false;
+                        if(!isDualTree)
+                        {
+                            TU_VALID = currTU.blocks[compIdx].valid();
+                            TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+                            if(TU_VALID)
+                            {
+                                TU_CBF = TU::getCbf(currTU, compID);
+                            }
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                        }
+                        else
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                        }
+
+                        if(BIF_chroma)
+                        {
+                            m_bilateralFilter.bilateralFilterDiamond5x5_chroma(srcYuv, resYuv, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                        }
+                        else
+                        {
+                            bool use_clip = isCb ? clipChromaIfNoBilat_Cb : clipChromaIfNoBilat_Cr;
+                            if(use_clip && currTU.blocks[compID].valid())
+                            {
+                                m_bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(srcYuv, resYuv, cs.slice->clpRng(compID), currTU, isCb);
+                            }
+                        }
+                    }
+                }
+            }
+      }
+      else
+      {
+         offsetCTU(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
+      }
 #else
       offsetCTU(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
 #endif
+#endif
 #endif
 
       }
@@ -1306,9 +1735,17 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
 #endif
   //reconstruct
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if (isGreedymergeEncoding || (cs.pps->getUseBIF() &&allBlksDisabled) || (cs.pps->getUseCBIF() &&allBlksDisabled))
+#else
   if (isGreedymergeEncoding || (cs.pps->getUseBIF() &&allBlksDisabled) )
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  if (isGreedymergeEncoding || (cs.pps->getUseCBIF() &&allBlksDisabled) )
 #else
   if (isGreedymergeEncoding)
+#endif
 #endif
   {
     ctuRsAddr = 0;
@@ -1336,6 +1773,27 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
               myResBlk[yy*myResStride+xx] = mySrcBlk[yy*mySrcStride+xx];
         }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(cs.pps->getUseCBIF()){
+            for(int chroma_idx = COMPONENT_Cb; chroma_idx < MAX_NUM_COMPONENT; chroma_idx++){
+
+                bool isCb = chroma_idx == COMPONENT_Cb ? true : false;
+                ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+
+                int  myResStride_chroma = resYuv.get(compID).stride;
+                const CompArea& myCompArea_chroma = area.block(compID);
+                Pel* myResBlk_chroma = resYuv.get(compID).bufAt(myCompArea_chroma);
+                int mySrcStride_chroma = srcYuv.get(compID).stride;
+                Pel* mySrcBlk_chroma = srcYuv.get(compID).bufAt(myCompArea_chroma);
+
+                for(int yy = 0; yy<area.chromaSize().height; yy++){
+                    for(int xx = 0; xx<area.chromaSize().width; xx++){
+                        myResBlk_chroma[yy*myResStride_chroma+xx] = mySrcBlk_chroma[yy*mySrcStride_chroma+xx];
+                    }
+                }
+            }
+        }
+#endif
 
 #if JVET_W0066_CCSAO
         offsetCTUnoClip(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
@@ -1357,8 +1815,210 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
           }
         }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(cs.pps->getUseCBIF())
+        {
+            CBifParams& CBifParams = cs.picture->getCBifParam();
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+            bool BIF_chroma = false;
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+
+                    for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                    {
+                        bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                        ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+                        bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                        BIF_chroma = false;
+                        if(!isDualTree)
+                        {
+                            TU_VALID = currTU.blocks[compIdx].valid();
+                            TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+
+                            if(TU_VALID)
+                            {
+                                TU_CBF = TU::getCbf(currTU, compID);
+                            }
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                        }
+                        else
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                        }
+                        if(BIF_chroma)
+                        {
+                            bilateralFilter.bilateralFilterDiamond5x5NoClip_chroma(srcYuv, resYuv, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                        }
+                    }
+                }
+            }
+        }
+#endif
 #else
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(cs.pps->getUseBIF() || cs.pps->getUseCBIF())
+        {
+            offsetCTUnoClip(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
+            SAOBlkParam mySAOblkParam = cs.picture->getSAO()[ctuRsAddr];
+            SAOOffset& myCtbOffset     = mySAOblkParam[0];
+            BifParams& bifParams = cs.picture->getBifParam();
+
+            bool clipLumaIfNoBilat = false;
+            if(myCtbOffset.modeIdc != SAO_MODE_OFF)
+                clipLumaIfNoBilat = true;
+
+            CBifParams& CBifParams = cs.picture->getCBifParam();
+            SAOOffset& myCtbOffset_Cb     = mySAOblkParam[1];
+            SAOOffset& myCtbOffset_Cr     = mySAOblkParam[2];
+            bool clipChromaIfNoBilat_Cb = false;
+            bool clipChromaIfNoBilat_Cr = false;
+
+            if(myCtbOffset_Cb.modeIdc != SAO_MODE_OFF)
+            {
+                clipChromaIfNoBilat_Cb = true;
+            }
+            if(myCtbOffset_Cr.modeIdc != SAO_MODE_OFF)
+            {
+                clipChromaIfNoBilat_Cr = true;
+            }
+            if(cs.pps->getUseBIF())
+            {
+                for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
+                {
+                    for (auto &currTU : CU::traverseTUs(currCU))
+                    {
+                        bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+                        if ( bifParams.ctuOn[ctuRsAddr] && ((TU::getCbf(currTU, COMPONENT_Y) || isInter == false) && (currTU.cu->qp > 17)) && (128 > std::max(currTU.lumaSize().width, currTU.lumaSize().height)) && ((isInter == false) || (32 > std::min(currTU.lumaSize().width, currTU.lumaSize().height))))
+                        {
+                            bilateralFilter.bilateralFilterDiamond5x5(srcYuv, resYuv, currTU.cu->qp, cs.slice->clpRng(COMPONENT_Y), currTU);
+                        }
+                        else
+                        {
+                            // We don't need to clip if SAO was not performed on luma.
+                            if(clipLumaIfNoBilat)
+                                bilateralFilter.clipNotBilaterallyFilteredBlocks(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Y), currTU);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
+                {
+                    for (auto &currTU : CU::traverseTUs(currCU))
+                    {
+                        if(clipLumaIfNoBilat)
+                            bilateralFilter.clipNotBilaterallyFilteredBlocks(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Y), currTU);
+                    }
+                }
+            }
+
+            if(cs.pps->getUseCBIF())
+            {
+                bool TU_VALID = false;
+                bool TU_CBF = false;
+                bool isDualTree = CS::isDualITree(cs);
+                ChannelType CType = isDualTree ? CH_C : CH_L;
+                bool BIF_chroma = false;
+
+                for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+                {
+                    bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                    if(!chroma_valid)
+                    {
+                        continue;
+                    }
+                    for (auto &currTU : CU::traverseTUs(currCU))
+                    {
+                        bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+
+                        for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                        {
+                            bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                            ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+                            bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                            BIF_chroma = false;
+                            if(!isDualTree)
+                            {
+                                TU_VALID = currTU.blocks[compIdx].valid();
+                                TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+
+                                if(TU_VALID)
+                                {
+                                    TU_CBF = TU::getCbf(currTU, compID);
+                                }
+                                BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                            }
+                            else
+                            {
+                                TU_CBF = TU::getCbf(currTU, compID);
+                                BIF_chroma = (CTU_ON && (( TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                            }
+                            if(BIF_chroma)
+                            {
+                                bilateralFilter.bilateralFilterDiamond5x5_chroma(srcYuv, resYuv, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                            }
+                            else
+                            {
+                                bool use_clip = isCb ? clipChromaIfNoBilat_Cb : clipChromaIfNoBilat_Cr;
+                                if(use_clip && currTU.blocks[compIdx].valid())
+                                {
+                                    bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(srcYuv, resYuv, cs.slice->clpRng(compID), currTU, isCb);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            else
+            {
+                bool isDualTree = CS::isDualITree(cs);
+                ChannelType CType = isDualTree ? CH_C : CH_L;
+
+                for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+                {
+                    bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                    if(!chroma_valid)
+                    {
+                        continue;
+                    }
+
+                    for (auto &currTU : CU::traverseTUs(currCU))
+                    {
+                        if(clipChromaIfNoBilat_Cb && currTU.blocks[COMPONENT_Cb].valid())
+                        {
+                            bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Cb), currTU, true);
+                        }
+                        if(clipChromaIfNoBilat_Cr && currTU.blocks[COMPONENT_Cr].valid())
+                        {
+                            bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Cr), currTU, false);
+                        }
+                    }
+                }
+            }
+        }//BIF = 1 OR CBIF = 1
+        else
+        {
+            offsetCTU(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
+        }
+#else
         if(cs.pps->getUseBIF())
         {
           offsetCTUnoClip(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
@@ -1400,17 +2060,131 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
           // We do not use BIF so we can use the regular SAO function call
           offsetCTU(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
         }
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(cs.pps->getUseCBIF())
+        {
+            offsetCTUnoClip(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
+
+            SAOBlkParam mySAOblkParam = cs.picture->getSAO()[ctuRsAddr];
+            CBifParams& CBifParams = cs.picture->getCBifParam();
+
+            SAOOffset& myCtbOffset     = mySAOblkParam[0];
+            bool clipLumaIfNoBilat = false;
+            if(myCtbOffset.modeIdc != SAO_MODE_OFF)
+            {
+                clipLumaIfNoBilat = true;
+            }
+
+            SAOOffset& myCtbOffset_Cb     = mySAOblkParam[1];
+            SAOOffset& myCtbOffset_Cr     = mySAOblkParam[2];
+            bool clipChromaIfNoBilat_Cb = false;
+            bool clipChromaIfNoBilat_Cr = false;
+
+            if(myCtbOffset_Cb.modeIdc != SAO_MODE_OFF)
+            {
+                clipChromaIfNoBilat_Cb = true;
+            }
+            if(myCtbOffset_Cr.modeIdc != SAO_MODE_OFF)
+            {
+                clipChromaIfNoBilat_Cr = true;
+            }
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CH_L), CH_L))
+            {
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    if(clipLumaIfNoBilat)
+                    {
+                        bilateralFilter.clipNotBilaterallyFilteredBlocks(srcYuv, resYuv, cs.slice->clpRng(COMPONENT_Y), currTU);
+                    }
+                }
+            }
+
+            bool TU_VALID = false;
+            bool TU_CBF = false;
+            bool isDualTree = CS::isDualITree(cs);
+            ChannelType CType = isDualTree ? CH_C : CH_L;
+            bool BIF_chroma = false;
+
+            for (auto &currCU : cs.traverseCUs(CS::getArea(cs, area, CType), CType))
+            {
+                bool chroma_valid = currCU.Cb().valid() && currCU.Cr().valid();
+                if(!chroma_valid)
+                {
+                    continue;
+                }
+                for (auto &currTU : CU::traverseTUs(currCU))
+                {
+                    bool isInter = (currCU.predMode == MODE_INTER) ? true : false;
+
+                    for(int compIdx = COMPONENT_Cb; compIdx < MAX_NUM_COMPONENT; compIdx++)
+                    {
+                        bool isCb = compIdx == COMPONENT_Cb ? true : false;
+                        ComponentID compID = isCb ? COMPONENT_Cb : COMPONENT_Cr;
+                        bool CTU_ON = isCb ? CBifParams.ctuOn_Cb[ctuRsAddr] : CBifParams.ctuOn_Cr[ctuRsAddr];
+
+                        BIF_chroma = false;
+                        if(!isDualTree)
+                        {
+                            TU_VALID = currTU.blocks[compIdx].valid();
+                            TU_CBF = false;//if CHROMA TU is not vaild, CBF must be zero
+                            if(TU_VALID)
+                            {
+                                TU_CBF = TU::getCbf(currTU, compID);
+                            }
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)) && (TU_VALID));
+                        }
+                        else
+                        {
+                            TU_CBF = TU::getCbf(currTU, compID);
+                            BIF_chroma = (CTU_ON && ((TU_CBF || isInter == false) && (currTU.cu->qp > 17)));
+                        }
+                        if(BIF_chroma)
+                        {
+                            bilateralFilter.bilateralFilterDiamond5x5_chroma(srcYuv, resYuv, currTU.cu->qp, cs.slice->clpRng(compID), currTU, isCb);
+                        }
+                        else
+                        {
+                            bool use_clip = isCb ? clipChromaIfNoBilat_Cb : clipChromaIfNoBilat_Cr;
+                            if(use_clip && currTU.blocks[compIdx].valid())
+                            {
+                                bilateralFilter.clipNotBilaterallyFilteredBlocks_chroma(srcYuv, resYuv, cs.slice->clpRng(compID), currTU, isCb);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            offsetCTU(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
+        }
 #else
         offsetCTU(area, srcYuv, resYuv, reconParams[ctuRsAddr], cs);
 #endif
+#endif
 #endif
         ctuRsAddr++;
       }
     }
     //delete memory
 #if JVET_V0094_BILATERAL_FILTER
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if(!(cs.pps->getUseBIF()) || !(cs.pps->getUseCBIF()) || !allBlksDisabled)
+    {
+#else
     if (!(cs.pps->getUseBIF()) ||  !allBlksDisabled)
     {
+#endif
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if (!(cs.pps->getUseCBIF()) ||  !allBlksDisabled)
+    {
+#else
+//     DO NOTHING
+#endif
 #endif
     for (uint32_t i = 0; i< groupBlkStat.size(); i++)
     {
@@ -1421,7 +2195,7 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
       delete[] groupBlkStat[i];
     }
     groupBlkStat.clear();
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
     }
 #endif
   }
@@ -1441,7 +2215,7 @@ void EncSampleAdaptiveOffset::decideBlkParams(CodingStructure& cs, bool* sliceEn
 
   EncSampleAdaptiveOffset::disabledRate( cs, reconParams, saoEncodingRate, saoEncodingRateChroma );
   
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   bilateralFilter.destroy();
 #endif
 }
@@ -1482,7 +2256,7 @@ void EncSampleAdaptiveOffset::disabledRate( CodingStructure& cs, SAOBlkParam* re
 
 void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int channelBitDepth, SAOStatData* statsDataTypes
                         , Pel* srcBlk, Pel* orgBlk,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                           Pel* bifBlk, int bifStride,
 #endif
                                           int srcStride, int orgStride, int width, int height
@@ -1495,7 +2269,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
   int8_t signLeft, signRight, signDown;
   int64_t *diff, *count;
   Pel *srcLine, *orgLine;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   Pel *bifLine;
 #endif
   int* skipLinesR = m_skipLinesR[compIdx];
@@ -1508,7 +2282,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
 
     srcLine = srcBlk;
     orgLine = orgBlk;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
     bifLine = bifBlk;
 #endif
     diff    = statsData.diff;
@@ -1540,7 +2314,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
             edgeType  =  signRight + signLeft;
             signLeft  = -signRight;
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
             diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
             diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1549,7 +2323,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
           }
           srcLine  += srcStride;
           orgLine  += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
           bifLine  += bifStride;
 #endif
         }
@@ -1574,7 +2348,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
                 edgeType  =  signRight + signLeft;
                 signLeft  = -signRight;
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                 diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
                 diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1583,7 +2357,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
               }
               srcLine  += srcStride;
               orgLine  += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
               bifLine  += bifStride;
 #endif
             }
@@ -1609,7 +2383,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
         {
           srcLine += srcStride;
           orgLine += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
           bifLine += bifStride;
 #endif
         }
@@ -1636,7 +2410,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
             edgeType  = signDown + signUpLine[x];
             signUpLine[x]= -signDown;
 
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
             diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
             diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1646,7 +2420,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
           }
           srcLine += srcStride;
           orgLine += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
           bifLine += bifStride;
 #endif
         }
@@ -1669,7 +2443,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
                   continue;
                 }
                 edgeType = sgn(srcLine[x] - srcLineBelow[x]) + sgn(srcLine[x] - srcLineAbove[x]);
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                 diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
                 diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1678,7 +2452,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
               }
               srcLine  += srcStride;
               orgLine  += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
               bifLine  += bifStride;
 #endif
             }
@@ -1723,7 +2497,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
             continue;
           }
           edgeType = sgn(srcLine[x] - srcLineAbove[x-1]) - signUpLine[x+1];
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
           diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
           diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1732,7 +2506,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
         }
         srcLine  += srcStride;
         orgLine  += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
         bifLine  += bifStride;
 #endif
         //middle lines
@@ -1749,7 +2523,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
               continue;
             }
             edgeType = signDown + signUpLine[x];
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
             diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
             diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1766,7 +2540,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
 
           srcLine += srcStride;
           orgLine += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
           bifLine += bifStride;
 #endif
         }
@@ -1789,7 +2563,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
                   continue;
                 }
                 edgeType = sgn(srcLine[x] - srcLineBelow[x+1]) + sgn(srcLine[x] - srcLineAbove[x-1]);
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                 diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
                 diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1798,7 +2572,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
               }
               srcLine  += srcStride;
               orgLine  += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
               bifLine  += bifStride;
 #endif
             }
@@ -1843,7 +2617,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
             continue;
           }
           edgeType = sgn(srcLine[x] - srcLineAbove[x+1]) - signUpLine[x-1];
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
           diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
           diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1853,7 +2627,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
 
         srcLine += srcStride;
         orgLine += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
         bifLine += bifStride;
 #endif
         //middle lines
@@ -1870,7 +2644,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
               continue;
             }
             edgeType = signDown + signUpLine[x];
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
             diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
             diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1882,7 +2656,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
           signUpLine[endX-1] = (int8_t)sgn(srcLineBelow[endX-1] - srcLine[endX]);
           srcLine  += srcStride;
           orgLine  += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
           bifLine  += bifStride;
 #endif
         }
@@ -1905,7 +2679,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
                   continue;
                 }
                 edgeType = sgn(srcLine[x] - srcLineBelow[x-1]) + sgn(srcLine[x] - srcLineAbove[x+1]);
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                 diff [edgeType] += (orgLine[x] - bifLine[x]);
 #else
                 diff [edgeType] += (orgLine[x] - srcLine[x]);
@@ -1914,7 +2688,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
               }
               srcLine  += srcStride;
               orgLine  += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
               bifLine  += bifStride;
 #endif
             }
@@ -1938,7 +2712,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
           {
 
             int bandIdx= srcLine[x] >> shiftBits;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
             diff [bandIdx] += (orgLine[x] - bifLine[x]);
 #else
             diff [bandIdx] += (orgLine[x] - srcLine[x]);
@@ -1947,7 +2721,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
           }
           srcLine += srcStride;
           orgLine += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
           bifLine += bifStride;
 #endif
         }
@@ -1963,7 +2737,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
               for (x=startX; x< endX; x++)
               {
                 int bandIdx= srcLine[x] >> shiftBits;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                 diff [bandIdx] += (orgLine[x] - bifLine[x]);
 #else
                 diff [bandIdx] += (orgLine[x] - srcLine[x]);
@@ -1972,7 +2746,7 @@ void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int c
               }
               srcLine  += srcStride;
               orgLine  += orgStride;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
               bifLine  += bifStride;
 #endif
             }
diff --git a/source/Lib/EncoderLib/EncSampleAdaptiveOffset.h b/source/Lib/EncoderLib/EncSampleAdaptiveOffset.h
index 693ec51c38199b2976e98978f6e016c834545c3b..3ebfb14c0d5343a580393636fd124df1747e0293 100644
--- a/source/Lib/EncoderLib/EncSampleAdaptiveOffset.h
+++ b/source/Lib/EncoderLib/EncSampleAdaptiveOffset.h
@@ -159,6 +159,9 @@ public:
                    const bool bTestSAODisableAtPictureLevel, const double saoEncodingRate, const double saoEncodingRateChroma, const bool isPreDBFSamplesUsed, bool isGreedyMergeEncoding
 #if JVET_V0094_BILATERAL_FILTER
                                           ,BIFCabacEst* BifCABACEstimator
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+                                          ,CBIFCabacEst* CBifCABACEstimator
 #endif
                   );
 #if JVET_W0066_CCSAO
@@ -170,7 +173,7 @@ private: //methods
 
   void deriveLoopFilterBoundaryAvailibility(CodingStructure& cs, const Position &pos, bool& isLeftAvail, bool& isAboveAvail, bool& isAboveLeftAvail) const;
   void getStatistics(std::vector<SAOStatData**>& blkStats, PelUnitBuf& orgYuv, PelUnitBuf& srcYuv,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                      PelUnitBuf& bifYuv,
 #endif
                      CodingStructure& cs, bool isCalculatePreDeblockSamples = false);
@@ -181,7 +184,7 @@ private: //methods
 #endif
                         const double saoEncodingRate, const double saoEncodingRateChroma, const bool isGreedymergeEncoding );
   void getBlkStats(const ComponentID compIdx, const int channelBitDepth, SAOStatData* statsDataTypes, Pel* srcBlk, Pel* orgBlk,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                    Pel* bifBlk, int bifStride,
 #endif
                    int srcStride, int orgStride, int width, int height, bool isLeftAvail,  bool isRightAvail, bool isAboveAvail, bool isBelowAvail, bool isAboveLeftAvail, bool isAboveRightAvail, bool isCalculatePreDeblockSamples
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index 3b84e46ed68e9a846f3a6a977be5c61059609594..13cb93d761e86bd12d34294ce360ace2f26a35cb 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -1939,6 +1939,14 @@ void EncSlice::encodeSlice   ( Picture* pcPic, OutputBitstream* pcSubstreams, ui
       BifParams& bifParam = cs.picture->getBifParam();
       m_CABACWriter->bif(*pcSlice, bifParam);
     }
+#endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    if(ctuRsAddr == 0)
+    {
+        CBifParams& CBifParam = cs.picture->getCBifParam();
+        m_CABACWriter->Cbif_Cb(*pcSlice, CBifParam);
+        m_CABACWriter->Cbif_Cr(*pcSlice, CBifParam);
+    }
 #endif
     m_CABACWriter->coding_tree_unit( cs, ctuArea, pcPic->m_prevQP, ctuRsAddr );
 
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index cd00b85217c8ce4f9a3acac90d53e63e6f6c528d..c31b94a72eefc07b3637302c4ae93f9b7c8f682d 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -45,7 +45,7 @@
 #include "CommonLib/UnitTools.h"
 #include "CommonLib/dtrace_next.h"
 #include "CommonLib/dtrace_buffer.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "CommonLib/BilateralFilter.h"
 #endif
 #include "CommonLib/MCTS.h"
@@ -92,7 +92,7 @@ InterSearch::InterSearch()
   , m_pSplitCS                    (nullptr)
   , m_pFullCS                     (nullptr)
   , m_pcEncCfg                    (nullptr)
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 , m_bilateralFilter             (nullptr)
 #endif
   , m_pcTrQuant                   (nullptr)
@@ -213,7 +213,7 @@ InterSearch::~InterSearch()
 }
 
 void InterSearch::init( EncCfg*        pcEncCfg,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                       BilateralFilter* bilateralFilter,
 #endif
                         TrQuant*       pcTrQuant,
@@ -238,7 +238,7 @@ void InterSearch::init( EncCfg*        pcEncCfg,
   }
   m_defaultCachedBvs.currCnt = 0;
   m_pcEncCfg                     = pcEncCfg;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_bilateralFilter              = bilateralFilter;
 #endif
   m_pcTrQuant                    = pcTrQuant;
@@ -7724,11 +7724,66 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par
             }
             else
             {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+              if(isChroma(compID))
+              {
+                if (cs.pps->getUseCBIF() && isChroma(compID) && (tu.cu->qp > 17))
+                {
+                    //chroma and bilateral
+                    CompArea tmpArea1(compID, tu.chromaFormat, Position(0, 0), Size(resiBuf.width, resiBuf.height));
+                    PelBuf tmpRecChroma = m_tmpStorageLCU.getBuf(tmpArea1);
+                    tmpRecChroma.copyFrom(resiBuf);
+
+                    const CPelBuf predBuf = csFull->getPredBuf(compArea);
+                    PelBuf recIPredBuf = csFull->slice->getPic()->getRecoBuf(compArea);
+                    bool isCb = compID == COMPONENT_Cb ? true : false;
+
+                    m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpRecChroma, predBuf, tmpRecChroma, tu.cu->qp, recIPredBuf, cs.slice->clpRng(compID), tu, false, isCb);
+
+                    currCompDist = m_pcRdCost->getDistPart(orgResiBuf, tmpRecChroma, channelBitDepth, compID, DF_SSE);
+                }
+                else
+                {   //chroma but not bilateral
+                    currCompDist = m_pcRdCost->getDistPart(orgResiBuf, resiBuf, channelBitDepth, compID, DF_SSE);
+                }
+              }
+              else
+              { //luma but not bilateral
+                currCompDist = m_pcRdCost->getDistPart(orgResiBuf, resiBuf, channelBitDepth, compID, DF_SSE);
+              }
+#else
               currCompDist = m_pcRdCost->getDistPart(orgResiBuf, resiBuf, channelBitDepth, compID, DF_SSE);
+#endif
+            }
+#else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+            if(isChroma(compID))
+            {
+                if(cs.pps->getUseCBIF() && isChroma(compID) && (tu.cu->qp > 17)){
+                    //chroma and bilateral
+                    CompArea tmpArea1(compID, tu.chromaFormat, Position(0, 0), Size(resiBuf.width, resiBuf.height));
+                    PelBuf tmpRecChroma = m_tmpStorageLCU.getBuf(tmpArea1);
+                    tmpRecChroma.copyFrom(resiBuf);
+
+                    const CPelBuf predBuf = csFull->getPredBuf(compArea);
+                    PelBuf recIPredBuf = csFull->slice->getPic()->getRecoBuf(compArea);
+                    bool isCb = compID == COMPONENT_Cb ? true : false;
+
+                    m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpRecChroma, predBuf, tmpRecChroma, tu.cu->qp, recIPredBuf, cs.slice->clpRng(compID), tu, false, isCb);
+
+                    currCompDist = m_pcRdCost->getDistPart(orgResiBuf, tmpRecChroma, channelBitDepth, compID, DF_SSE);
+                }
+                else{//chroma but not bilateral
+                        currCompDist = m_pcRdCost->getDistPart(orgResiBuf, resiBuf, channelBitDepth, compID, DF_SSE);
+                }
+            }
+            else{//luma but not bilateral
+                currCompDist = m_pcRdCost->getDistPart(orgResiBuf, resiBuf, channelBitDepth, compID, DF_SSE);
             }
 #else
             currCompDist = m_pcRdCost->getDistPart(orgResiBuf, resiBuf, channelBitDepth, compID, DF_SSE);
 #endif
+#endif
 
 #if WCG_EXT
             currCompCost = m_pcRdCost->calcRdCost(currCompFracBits, currCompDist, false);
@@ -8731,6 +8786,34 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa
         }
       }
     }
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    PelBuf tmpRecChroma;
+    if(isChroma(compID)){
+
+        bool isCb = compID == COMPONENT_Cb ? true : false;
+        const CompArea &areaUV = isCb ? cu.Cb() : cu.Cr();
+        CompArea      tmpArea2(isCb ? COMPONENT_Cb : COMPONENT_Cr, areaUV.chromaFormat, Position(0, 0), areaUV.size());
+        tmpRecChroma = m_tmpStorageLCU.getBuf(tmpArea2);
+        tmpRecChroma.copyFrom(reco);
+
+        if(cs.pps->getUseCBIF() && isChroma(compID) && (cu.qp > 17))
+        {
+            for (auto &currTU : CU::traverseTUs(cu))
+            {
+                Position tuPosInCu = currTU.chromaPos() - cu.chromaPos();
+                PelBuf tmpSubBuf = tmpRecChroma.subBuf(tuPosInCu, currTU.chromaSize());
+                bool isInter = (cu.predMode == MODE_INTER) ? true : false;
+
+                if ((TU::getCbf(currTU, isCb ? COMPONENT_Cb : COMPONENT_Cr) || isInter == false))
+                {
+                    CompArea compArea = currTU.blocks[compID];
+                    PelBuf recIPredBuf = cs.slice->getPic()->getRecoBuf(compArea);
+                    m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpSubBuf, tmpSubBuf, tmpSubBuf, currTU.cu->qp, recIPredBuf, cs.slice->clpRng(compID), currTU, true, isCb);
+                }
+            }
+        }
+    }
+#endif
 #if WCG_EXT
     if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (
       m_pcEncCfg->getLmcs() && (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())))
@@ -8745,7 +8828,13 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa
         finalDistortion += m_pcRdCost->getDistPart(org, tmpRecLuma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
       }
       else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      {
+          finalDistortion += m_pcRdCost->getDistPart(org, tmpRecChroma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
+      }
+#else
         finalDistortion += m_pcRdCost->getDistPart(org, reco, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
+#endif
     }
     else
 #endif
@@ -8756,10 +8845,43 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa
       }
       else
       {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        finalDistortion += m_pcRdCost->getDistPart( org, tmpRecChroma, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE );
+#else
         finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE );
+#endif
       }
     }
 #else
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    PelBuf tmpRecChroma;
+    if(isChroma(compID))
+    {
+        bool isCb = compID == COMPONENT_Cb ? true : false;
+        const CompArea &areaUV = isCb ? cu.Cb() : cu.Cr();
+        CompArea      tmpArea2(isCb ? COMPONENT_Cb : COMPONENT_Cr, areaUV.chromaFormat, Position(0, 0), areaUV.size());
+        tmpRecChroma = m_tmpStorageLCU.getBuf(tmpArea2);
+        tmpRecChroma.copyFrom(reco);
+
+        if(cs.pps->getUseCBIF() && isChroma(compID) && (cu.qp > 17))
+        {
+
+            for (auto &currTU : CU::traverseTUs(cu))
+            {
+                Position tuPosInCu = currTU.chromaPos() - cu.chromaPos();
+                PelBuf tmpSubBuf = tmpRecChroma.subBuf(tuPosInCu, currTU.chromaSize());
+                bool isInter = (cu.predMode == MODE_INTER) ? true : false;
+
+                if ((TU::getCbf(currTU, isCb ? COMPONENT_Cb : COMPONENT_Cr) || isInter == false))
+                {
+                    CompArea compArea = currTU.blocks[compID];
+                    PelBuf recIPredBuf = cs.slice->getPic()->getRecoBuf(compArea);
+                    m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpSubBuf, tmpSubBuf, tmpSubBuf, currTU.cu->qp, recIPredBuf, cs.slice->clpRng(compID), currTU, true, isCb);
+                }
+            }
+        }
+    }
+#endif
 #if WCG_EXT
     if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcEncCfg->getLmcs() && (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())))
     {
@@ -8774,13 +8896,32 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa
       }
       else
       {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+          if(isChroma(compID)){
+              finalDistortion += m_pcRdCost->getDistPart(org, tmpRecChroma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
+          }
+          else{
+              finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE_WTD, &orgLuma );
+          }
+#else
         finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE_WTD, &orgLuma );
+#endif
       }
     }
     else
 #endif
     {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if(isChroma(compID)){
+        finalDistortion += m_pcRdCost->getDistPart( org, tmpRecChroma, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE );
+      }
+      else
+      {
+        finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE );
+      }
+#else
       finalDistortion += m_pcRdCost->getDistPart( org, reco, sps.getBitDepth( toChannelType( compID ) ), compID, DF_SSE );
+#endif
     }
 #endif
   }
diff --git a/source/Lib/EncoderLib/InterSearch.h b/source/Lib/EncoderLib/InterSearch.h
index beee89f4936292a336fb1348a85304e1bfc80a97..e512139944d80399fa73c53dea3e81d2326411a6 100644
--- a/source/Lib/EncoderLib/InterSearch.h
+++ b/source/Lib/EncoderLib/InterSearch.h
@@ -48,7 +48,7 @@
 #include "CommonLib/Unit.h"
 #include "CommonLib/UnitPartitioner.h"
 #include "CommonLib/RdCost.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "CommonLib/BilateralFilter.h"
 #endif
 
@@ -312,7 +312,7 @@ protected:
   EncCfg*         m_pcEncCfg;
 
   // interface to classes
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter* m_bilateralFilter;
 #endif
   TrQuant*        m_pcTrQuant;
@@ -391,7 +391,7 @@ public:
   virtual ~InterSearch();
 
   void init                         ( EncCfg*        pcEncCfg,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                                      BilateralFilter* bilateralFilter,
 #endif
                                       TrQuant*       pcTrQuant,
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 770e6e2d413082f7cf43fc00ced523471f8b8bbb..e973656233027144567ca5ffca3ec63235802aad 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -43,7 +43,7 @@
 #include "CommonLib/Rom.h"
 #include "CommonLib/Picture.h"
 #include "CommonLib/UnitTools.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "CommonLib/BilateralFilter.h"
 #endif
 
@@ -60,7 +60,7 @@ IntraSearch::IntraSearch()
   , m_pFullCS       (nullptr)
   , m_pBestCS       (nullptr)
   , m_pcEncCfg      (nullptr)
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   , m_bilateralFilter(nullptr)
 #endif
   , m_pcTrQuant     (nullptr)
@@ -216,7 +216,7 @@ IntraSearch::~IntraSearch()
 }
 
 void IntraSearch::init( EncCfg*        pcEncCfg,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                        BilateralFilter* bilateralFilter,
 #endif
                         TrQuant*       pcTrQuant,
@@ -232,7 +232,7 @@ void IntraSearch::init( EncCfg*        pcEncCfg,
 {
   CHECK(m_isInitialized, "Already initialized");
   m_pcEncCfg                     = pcEncCfg;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   m_bilateralFilter              = bilateralFilter;
 #endif
   m_pcTrQuant                    = pcTrQuant;
@@ -3619,7 +3619,7 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
 
   const CompArea      &area                 = tu.blocks[compID];
   const SPS           &sps                  = *cs.sps;
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   const PPS           &pps                  = *cs.pps;
 #endif
 
@@ -3954,7 +3954,15 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
     tmpRecLuma = m_tmpStorageLCU.getBuf(tmpArea1);
     tmpRecLuma.copyFrom(piReco);
   }
-  
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  CompArea tmpArea2(compID, area.chromaFormat, Position(0, 0), area.size());
+  PelBuf tmpRecChroma;
+  if(isChroma(compID))
+  {
+    tmpRecChroma = m_tmpStorageLCU.getBuf(tmpArea2);
+    tmpRecChroma.copyFrom(piReco);
+  }
+#endif
   //===== update distortion =====
 #if WCG_EXT
   
@@ -3989,10 +3997,32 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
     }
     else
     {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if(pps.getUseCBIF() && isChroma(compID) && (tu.cu->qp > 17))
+      {
+        CompArea compArea = tu.blocks[compID];
+        PelBuf recIPredBuf = cs.slice->getPic()->getRecoBuf(compArea);
+        bool isCb = compID == COMPONENT_Cb ? true : false;
+        m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpRecChroma, tmpRecChroma, tmpRecChroma, tu.cu->qp, recIPredBuf, cs.slice->clpRng(compID), tu, true, isCb);
+      }
+      ruiDist += m_pcRdCost->getDistPart(piOrg, tmpRecChroma, bitDepth, compID, DF_SSE_WTD, &orgLuma);
+#else
       ruiDist += m_pcRdCost->getDistPart(piOrg, piReco, bitDepth, compID, DF_SSE_WTD, &orgLuma);
+#endif
       if( jointCbCr )
       {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(compID == COMPONENT_Cr)
+        {
+            ruiDist += m_pcRdCost->getDistPart(crOrg, tmpRecChroma, bitDepth, COMPONENT_Cr, DF_SSE_WTD, &orgLuma);
+        }
+        else
+        {
+            ruiDist += m_pcRdCost->getDistPart(crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE_WTD, &orgLuma);
+        }
+#else
         ruiDist += m_pcRdCost->getDistPart(crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE_WTD, &orgLuma);
+#endif
       }
     }
   }
@@ -4013,16 +4043,120 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
     }
     else
     {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+      if (pps.getUseCBIF() && isChroma(compID) && (tu.cu->qp > 17))
+      {
+        CompArea compArea = tu.blocks[compID];
+        PelBuf recIPredBuf = cs.slice->getPic()->getRecoBuf(compArea);
+        bool isCb = compID == COMPONENT_Cb ? true : false;
+        m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpRecChroma, tmpRecChroma, tmpRecChroma, tu.cu->qp, recIPredBuf, cs.slice->clpRng(compID), tu, true, isCb);
+      }
+      ruiDist += m_pcRdCost->getDistPart( piOrg, tmpRecChroma, bitDepth, compID, DF_SSE );
+#else
       ruiDist += m_pcRdCost->getDistPart( piOrg, piReco, bitDepth, compID, DF_SSE );
+#endif
       if( jointCbCr )
       {
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+        if(compID == COMPONENT_Cr)
+        {
+            ruiDist += m_pcRdCost->getDistPart( crOrg, tmpRecChroma, bitDepth, COMPONENT_Cr, DF_SSE );
+        }
+        else{
+            ruiDist += m_pcRdCost->getDistPart( crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE );
+        }
+#else
         ruiDist += m_pcRdCost->getDistPart( crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE );
+#endif
       }
     }
   }
 
 #else
   //===== update distortion =====
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+    CompArea tmpArea2(compID, area.chromaFormat, Position(0, 0), area.size());
+    PelBuf tmpRecChroma;
+    if(isChroma(compID))
+    {
+        tmpRecChroma = m_tmpStorageLCU.getBuf(tmpArea2);
+        tmpRecChroma.copyFrom(piReco);
+    }
+#if WCG_EXT
+    if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcEncCfg->getLmcs() && slice.getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || (isChroma(compID) && m_pcEncCfg->getReshapeIntraCMD()))))
+    {
+        const CPelBuf orgLuma = cs.getOrgBuf( cs.area.blocks[COMPONENT_Y] );
+        if(isLuma(compID))
+        {
+            if (compID == COMPONENT_Y  && !(m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()))
+            {
+                CompArea      tmpArea1(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size());
+                PelBuf tmpRecLuma = m_tmpStorageLCU.getBuf(tmpArea1);
+                tmpRecLuma.rspSignal( piReco, m_pcReshape->getInvLUT() );
+                ruiDist += m_pcRdCost->getDistPart(piOrg, tmpRecLuma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
+            }
+            else
+            {
+                ruiDist += m_pcRdCost->getDistPart(piOrg, piReco, bitDepth, compID, DF_SSE_WTD, &orgLuma);
+                if( jointCbCr )
+                {
+                    ruiDist += m_pcRdCost->getDistPart(crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE_WTD, &orgLuma);
+                }
+            }
+        }
+        else
+        {
+            if(pps.getUseCBIF() && isChroma(compID) && (tu.cu->qp > 17))
+            {
+                CompArea compArea = tu.blocks[compID];
+                PelBuf recIPredBuf = cs.slice->getPic()->getRecoBuf(compArea);
+                bool isCb = compID == COMPONENT_Cb ? true : false;
+                m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpRecChroma, tmpRecChroma, tmpRecChroma, tu.cu->qp, recIPredBuf, cs.slice->clpRng(compID), tu, true, isCb);
+            }
+            ruiDist += m_pcRdCost->getDistPart(piOrg, tmpRecChroma, bitDepth, compID, DF_SSE_WTD, &orgLuma);
+
+            if( jointCbCr )
+            {
+                if(compID == COMPONENT_Cr)
+                {
+                    ruiDist += m_pcRdCost->getDistPart(crOrg, tmpRecChroma, bitDepth, COMPONENT_Cr, DF_SSE_WTD, &orgLuma);
+                }
+                else
+                {
+                    ruiDist += m_pcRdCost->getDistPart(crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE_WTD, &orgLuma);
+                }
+            }
+        }
+    }
+    else
+#endif
+    {
+        if(isLuma(compID))
+        {
+            ruiDist += m_pcRdCost->getDistPart( piOrg, piReco, bitDepth, compID, DF_SSE );
+        }
+        else{
+            if (pps.getUseCBIF() && isChroma(compID) && (tu.cu->qp > 17))
+            {
+                CompArea compArea = tu.blocks[compID];
+                PelBuf recIPredBuf = cs.slice->getPic()->getRecoBuf(compArea);
+                bool isCb = compID == COMPONENT_Cb ? true : false;
+                m_bilateralFilter->bilateralFilterRDOdiamond5x5_chroma(tmpRecChroma, tmpRecChroma, tmpRecChroma, tu.cu->qp, recIPredBuf, cs.slice->clpRng(compID), tu, true, isCb);
+            }
+            ruiDist += m_pcRdCost->getDistPart( piOrg, tmpRecChroma, bitDepth, compID, DF_SSE );
+        }
+        if( jointCbCr )
+        {
+            if(compID == COMPONENT_Cr)
+            {
+                ruiDist += m_pcRdCost->getDistPart( crOrg, tmpRecChroma, bitDepth, COMPONENT_Cr, DF_SSE );
+            }
+            else{
+                ruiDist += m_pcRdCost->getDistPart( crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE );
+            }
+        }
+    }
+#else
 #if WCG_EXT
   if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcEncCfg->getLmcs()
     && slice.getLmcsEnabledFlag() && (m_pcReshape->getCTUFlag() || (isChroma(compID) && m_pcEncCfg->getReshapeIntraCMD()))))
@@ -4054,6 +4188,7 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
     }
   }
 #endif
+#endif
 }
 
 void IntraSearch::xIntraCodingACTTUBlock(TransformUnit &tu, const ComponentID &compID, Distortion& ruiDist, std::vector<TrMode>* trModes, const bool loadTr)
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index 1d874aa7061039dcb83a74393af85a5a0ffc247b..be55616aa4f875e8329185a6f6ff91f5124fe117 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -47,7 +47,7 @@
 #include "CommonLib/TrQuant.h"
 #include "CommonLib/Unit.h"
 #include "CommonLib/RdCost.h"
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
 #include "CommonLib/BilateralFilter.h"
 #endif
 #include "EncReshape.h"
@@ -404,7 +404,7 @@ protected:
   EncCfg*         m_pcEncCfg;
 
   // interface to classes
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
   BilateralFilter* m_bilateralFilter;
 #endif
   TrQuant*        m_pcTrQuant;
@@ -434,7 +434,7 @@ public:
   ~IntraSearch();
 
   void init                       ( EncCfg*        pcEncCfg,
-#if JVET_V0094_BILATERAL_FILTER
+#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                                    BilateralFilter* bilateralFilter,
 #endif
                                     TrQuant*       pcTrQuant,
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 42fc01cd0736bc0a0b916f9d76639676ad200a8f..d142ffb9211ce8b669c2b1629f5d9e6459787014 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -462,6 +462,14 @@ void HLSWriter::codePPS( const PPS* pcPPS )
     WRITE_SVLC( pcPPS->getBIFQPOffset(),                                  "bilateral_filter_qp_offset");
   }
 #endif
+#if JVET_X0071_CHROMA_BILATERAL_FILTER
+  WRITE_FLAG(pcPPS->getUseCBIF() ? 1 : 0, "chroma bilateral_filter_flag" );
+  if(pcPPS->getUseCBIF())
+  {
+    WRITE_CODE( pcPPS->getCBIFStrength(), 2,  "chroma bilateral_filter_strength");
+    WRITE_SVLC( pcPPS->getCBIFQPOffset(),     "chroma bilateral_filter_qp_offset");
+  }
+#endif
 #if !JVET_S0132_HLS_REORDER
   WRITE_FLAG( pcPPS->getUseWP() ? 1 : 0,  "weighted_pred_flag" );   // Use of Weighting Prediction (P_SLICE)
   WRITE_FLAG( pcPPS->getWPBiPred() ? 1 : 0, "weighted_bipred_flag" );  // Use of Weighting Bi-Prediction (B_SLICE)
@@ -617,12 +625,19 @@ void HLSWriter::codeAlfAps( APS* pcAPS )
     WRITE_FLAG( param.filterType[CHANNEL_TYPE_LUMA] == ALF_FILTER_9_EXT ? 1 : 0, "alf_luma_ext" );
     for (int altIdx = 0; altIdx < param.numAlternativesLuma; ++altIdx)
     {
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+      WRITE_FLAG( param.lumaClassifierIdx[altIdx], "alf_luma_classifier" );
+#endif
       WRITE_FLAG( param.nonLinearFlag[CHANNEL_TYPE_LUMA][altIdx], "alf_luma_clip" );
       WRITE_UVLC( param.numLumaFilters[altIdx] - 1, "alf_luma_num_filters_signalled_minus1" );
       if ( param.numLumaFilters[altIdx] > 1 )
       {
         const int length = ceilLog2(param.numLumaFilters[altIdx]);
+#if JVET_X0071_ALF_BAND_CLASSIFIER
+        for( int i = 0; i < ALF_NUM_CLASSES_CLASSIFIER[(int)param.lumaClassifierIdx[altIdx]]; i++ )
+#else
         for( int i = 0; i < MAX_NUM_ALF_CLASSES; i++ )
+#endif
         {
           WRITE_CODE( param.filterCoeffDeltaIdx[altIdx][i], length, "alf_luma_coeff_delta_idx" );
         }