diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 80371e5704dacce3dccfff3edc165b67b6b9a0bc..55bf7dc1589e42fc2d44b4f55ddc8834bb74e7f6 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -51,6 +51,7 @@
 #include <cassert>
 
 //########### place macros to be removed in next cycle below this line ###############
+#define JVET_R0327_ONE_PASS_CCALF                         1 // JVET-R0327: One-pass CCALF
 
 #define JVET_R0271_SLICE_LEVEL_DQ_SDH_RRC                 1 // JVET-R0271/R0155: Slice level DQ and SDH granularity for mixed lossy/lossless.
 
diff --git a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp
index e7d06837ff7f6ce3d069d14efa0453328e8e88f1..e2332457d168faf8a183904903d1a95f4ef9a78f 100644
--- a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp
+++ b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.cpp
@@ -1077,6 +1077,13 @@ void EncAdaptiveLoopFilter::ALFProcess(CodingStructure& cs, const double *lambda
   m_tempBuf.get(COMPONENT_Cr).copyFrom(cs.getRecoBuf().get(COMPONENT_Cr));
   recYuv = m_tempBuf.getBuf(cs.area);
   recYuv.extendBorderPel(MAX_ALF_FILTER_LENGTH >> 1);
+  
+#if JVET_R0327_ONE_PASS_CCALF
+  deriveStatsForCcAlfFiltering(orgYuv, recYuv, COMPONENT_Cb, m_numCTUsInWidth, (0 + 1), cs);
+  deriveStatsForCcAlfFiltering(orgYuv, recYuv, COMPONENT_Cr, m_numCTUsInWidth, (0 + 1), cs);
+  initDistortionCcalf();
+#endif
+
   m_CABACEstimator->getCtx() = SubCtx(Ctx::CcAlfFilterControlFlag, ctxStartCcAlf);
   deriveCcAlfFilter(cs, COMPONENT_Cb, orgYuv, recYuv, cs.getRecoBuf());
   m_CABACEstimator->getCtx() = SubCtx(Ctx::CcAlfFilterControlFlag, ctxStartCcAlf);
@@ -2538,6 +2545,20 @@ void  EncAdaptiveLoopFilter::initDistortion()
     }
   }
 }
+
+#if JVET_R0327_ONE_PASS_CCALF
+void  EncAdaptiveLoopFilter::initDistortionCcalf()
+{
+  for (int comp = 1; comp < MAX_NUM_COMPONENT; comp++)
+  {
+    for (int ctbIdx = 0; ctbIdx < m_numCTUsInPic; ctbIdx++)
+    {
+      m_ctbDistortionUnfilter[comp][ctbIdx] = m_alfCovarianceCcAlf[comp - 1][0][0][ctbIdx].pixAcc;
+    }
+  }
+}
+#endif
+
 void  EncAdaptiveLoopFilter::alfEncoderCtb(CodingStructure& cs, AlfParam& alfParamNewFilters
 #if ENABLE_QPA
   , const double lambdaChromaWeight
@@ -3568,6 +3589,36 @@ std::vector<int> EncAdaptiveLoopFilter::getAvailableCcAlfApsIds(CodingStructure&
   return result;
 }
 
+#if JVET_R0327_ONE_PASS_CCALF
+void EncAdaptiveLoopFilter::getFrameStatsCcalf(ComponentID compIdx, int filterIdc)
+{
+        int ctuRsAddr = 0;
+  const int filterIdx = filterIdc - 1;
+
+  // init Frame stats buffers
+  for (int shape = 0; shape != m_filterShapesCcAlf[compIdx - 1].size(); shape++)
+  {
+    m_alfCovarianceFrameCcAlf[compIdx - 1][shape][filterIdx].reset();
+  }
+
+  for (int yPos = 0; yPos < m_picHeight; yPos += m_maxCUHeight)
+  {
+    for (int xPos = 0; xPos < m_picWidth; xPos += m_maxCUWidth)
+    {
+      if (m_trainingCovControl[ctuRsAddr] == filterIdc)
+      {
+        for (int shape = 0; shape != m_filterShapesCcAlf[compIdx - 1].size(); shape++)
+        {
+          m_alfCovarianceFrameCcAlf[compIdx - 1][shape][filterIdx] +=
+            m_alfCovarianceCcAlf[compIdx - 1][shape][0][ctuRsAddr];
+        }
+      }
+      ctuRsAddr++;
+    }
+  }
+}
+#endif
+
 void EncAdaptiveLoopFilter::deriveCcAlfFilter( CodingStructure& cs, ComponentID compID, const PelUnitBuf& orgYuv, const PelUnitBuf& tempDecYuvBuf, const PelUnitBuf& dstYuv )
 {
   if (!cs.slice->getTileGroupAlfEnabledFlag(COMPONENT_Y))
@@ -3624,8 +3675,10 @@ void EncAdaptiveLoopFilter::deriveCcAlfFilter( CodingStructure& cs, ComponentID
   const Pel *unfiltered         = dstYuv.get( compID ).bufAt(0,0);
   const int orgStride           = orgYuv.get( compID ).stride;
   const int unfilteredStride    = dstYuv.get( compID ).stride;
+#if !JVET_R0327_ONE_PASS_CCALF
   const Pel *filtered           = m_buf->bufAt(0,0);
   const int filteredStride      = m_buf->stride;
+#endif
   uint64_t unfilteredDistortion = 0;
   computeLog2BlockSizeDistortion(org, orgStride, unfiltered, unfilteredStride, m_buf->height, m_buf->width,
                                  m_unfilteredDistortion[0], m_numCTUsInWidth, cs.pcv->maxCUWidthLog2 - scaleX,
@@ -3717,9 +3770,27 @@ void EncAdaptiveLoopFilter::deriveCcAlfFilter( CodingStructure& cs, ComponentID
           {
             if (!referencingExistingAps)
             {
+#if JVET_R0327_ONE_PASS_CCALF
+              getFrameStatsCcalf(compID, (filterIdx + 1));
+#else
               deriveStatsForCcAlfFiltering(orgYuv, tempDecYuvBuf, compID, m_numCTUsInWidth, (filterIdx + 1), cs);
+#endif
               deriveCcAlfFilterCoeff(compID, dstYuv, tempDecYuvBuf, ccAlfFilterCoeff, filterIdx);
             }
+#if JVET_R0327_ONE_PASS_CCALF
+            const int numCoeff  = m_filterShapesCcAlf[compID - 1][0].numCoeff - 1;
+            int log2BlockWidth  = cs.pcv->maxCUWidthLog2 - scaleX;
+            int log2BlockHeight = cs.pcv->maxCUHeightLog2 - scaleY;
+            for (int y = 0; y < m_buf->height; y += (1 << log2BlockHeight))
+            {
+              for (int x = 0; x < m_buf->width; x += (1 << log2BlockWidth))
+              {
+                int ctuIdx = (y >> log2BlockHeight) * m_numCTUsInWidth + (x >> log2BlockWidth);
+                m_trainingDistortion[filterIdx][ctuIdx] = int(m_ctbDistortionUnfilter[compID][ctuIdx] +
+                  m_alfCovarianceCcAlf[compID - 1][0][0][ctuIdx].calcErrorForCcAlfCoeffs((int*)(ccAlfFilterCoeff[filterIdx]), numCoeff, (m_scaleBits + 1)));
+              }
+            }
+#else
             m_buf->copyFrom(dstYuv.get(compID));
             applyCcAlfFilter(cs, compID, *m_buf, tempDecYuvBuf, nullptr, ccAlfFilterCoeff, filterIdx);
 
@@ -3727,6 +3798,7 @@ void EncAdaptiveLoopFilter::deriveCcAlfFilter( CodingStructure& cs, ComponentID
             computeLog2BlockSizeDistortion(
                                            org, orgStride, filtered, filteredStride, m_buf->height, m_buf->width, m_trainingDistortion[filterIdx],
                                            m_numCTUsInWidth, cs.pcv->maxCUWidthLog2 - scaleX, cs.pcv->maxCUHeightLog2 - scaleY, distortion);
+#endif
           }
         }
 
@@ -3900,7 +3972,9 @@ void EncAdaptiveLoopFilter::deriveStatsForCcAlfFiltering(const PelUnitBuf &orgYu
   {
     for (int xPos = 0; xPos < m_picWidth; xPos += m_maxCUWidth)
     {
+#if !JVET_R0327_ONE_PASS_CCALF
       if (m_trainingCovControl[ctuRsAddr] == filterIdc)
+#endif
       {
         const int width             = (xPos + m_maxCUWidth > m_picWidth) ? (m_picWidth - xPos) : m_maxCUWidth;
         const int height            = (yPos + m_maxCUHeight > m_picHeight) ? (m_picHeight - yPos) : m_maxCUHeight;
diff --git a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h
index 6aad7ca7a7e6952d1f0216cb0d420ea696362fee..aa399955e16d14b1f96c11675b49c6e83d082951 100644
--- a/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h
+++ b/source/Lib/EncoderLib/EncAdaptiveLoopFilter.h
@@ -234,7 +234,7 @@ private:
   uint8_t*               m_ctuEnableFlagTmp[MAX_NUM_COMPONENT];
   uint8_t*               m_ctuEnableFlagTmp2[MAX_NUM_COMPONENT];
   uint8_t*               m_ctuAlternativeTmp[MAX_NUM_COMPONENT];
-  AlfCovariance***       m_alfCovarianceCcAlf[2];           // [compIdx-1][shapeIdx][ctbAddr][filterIdx]
+  AlfCovariance***       m_alfCovarianceCcAlf[2];           // [compIdx-1][shapeIdx][filterIdx][ctbAddr]
   AlfCovariance**        m_alfCovarianceFrameCcAlf[2];      // [compIdx-1][shapeIdx][filterIdx]
 
   //for RDO
@@ -378,6 +378,10 @@ private:
   void xSetupCcAlfAPS( CodingStructure& cs );
   void countLumaSwingGreaterThanThreshold(const Pel* luma, int lumaStride, int height, int width, int log2BlockWidth, int log2BlockHeight, uint64_t* lumaSwingGreaterThanThresholdCount, int lumaCountStride);
   void countChromaSampleValueNearMidPoint(const Pel* chroma, int chromaStride, int height, int width, int log2BlockWidth, int log2BlockHeight, uint64_t* chromaSampleCountNearMidPoint, int chromaSampleCountNearMidPointStride);
+#if JVET_R0327_ONE_PASS_CCALF
+  void getFrameStatsCcalf(ComponentID compIdx, int filterIdc);
+  void initDistortionCcalf();
+#endif
 };