<div dir="ltr">Forgot to mention that it is a output changing commit. Some test results are given below.<div>Both bitrate and PSNR drops and SSIM increases slightly.<br><div><br></div><div><div><b>Before(crowd_run, medium)</b></div><div>encoded 500 frames in 75.17s (6.65 fps), 9689.38 kb/s, Avg QP:37.79, Global PSNR : 31.045, SSIM Mean Y: 0.8418636 ( 8.010 dB)<br></div><div><b>After</b> </div><div>encoded 500 frames in 77.56s (6.45 fps), 9698.85 kb/s, Avg QP:37.80, Global PSNR : 31.043, SSIM Mean Y: 0.8419146 ( 8.011 dB)</div><div><br></div><div><b>Before(crowd_run, veryslow)</b></div><div>encoded 500 frames in 946.15s (0.53 fps), 9504.15 kb/s, Avg QP:37.79, Global PSNR: 31.503, SSIM Mean Y: 0.8496565 ( 8.229 dB)<br></div><div><b>After</b></div><div>encoded 500 frames in 892.26s (0.56 fps), 9504.96 kb/s, Avg QP:37.79, Global PSNR: 31.502, SSIM Mean Y: 0.8497315 ( 8.231 dB)</div><div><br></div><div><b>Before(crowd_run, veryslow, --bitrate 7000)</b></div><div>encoded 500 frames in 765.03s (0.65 fps), 6944.96 kb/s, Avg QP:39.73, Global PSNR: 30.432, SSIM Mean Y: 0.8164274 ( 7.362 dB)</div><div><b>After</b> </div><div>encoded 500 frames in 770.37s (0.65 fps), 6943.92 kb/s, Avg QP:39.73, Global PSNR: 30.428, SSIM Mean Y: 0.8164941 ( 7.364 dB)<br></div><div><br></div><div><b>Before(crowd_run, medium, --bitrate 7000)</b></div><div>encoded 500 frames in 70.02s (7.14 fps), 6948.83 kb/s, Avg QP:39.76, Global PSNR : 29.972, SSIM Mean Y: 0.8075512 ( 7.157 dB)</div><div><b>After </b></div><div>encoded 500 frames in 68.45s (7.30 fps), 6947.25 kb/s, Avg QP:39.78, Global PSNR : 29.968, SSIM Mean Y: 0.8075448 ( 7.157 dB)</div><div><br></div><div><b>Before(crowd_run, medium, 2160p, --bitrate 7000)</b></div><div>encoded 500 frames in 264.31s (1.89 fps), 6962.31 kb/s, Avg QP:45.10, Global PSNR: 30.257, SSIM Mean Y: 0.7517543 ( 6.051 dB)</div><div><b>After </b><br></div><div>encoded 500 frames in 238.61s (2.10 fps), 6963.57 kb/s, Avg QP:45.10, Global PSNR: 30.259, SSIM Mean Y: 0.7529944 ( 6.073 dB)</div></div></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Apr 6, 2016 at 2:29 PM,  <span dir="ltr"><<a href="mailto:ashok@multicorewareinc.com" target="_blank">ashok@multicorewareinc.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"># HG changeset patch<br>
# User Ashok Kumar Mishra<<a href="mailto:ashok@multicorewareinc.com">ashok@multicorewareinc.com</a>><br>
# Date 1459933165 -19800<br>
#      Wed Apr 06 14:29:25 2016 +0530<br>
# Node ID a747b7c2d77bdc2037020019d99a8d3c09d532c8<br>
# Parent  511241d3ee7d6d53a999d46881e7921a601fa0e9<br>
SAO: modified sao rdo cost calculation<br>
sao type, eo and bo class are all fixed bit syntax elements, so no need<br>
to load the context repeatedly.<br>
<br>
diff -r 511241d3ee7d -r a747b7c2d77b source/encoder/entropy.cpp<br>
--- a/source/encoder/entropy.cpp        Wed Apr 06 13:12:17 2016 +0530<br>
+++ b/source/encoder/entropy.cpp        Wed Apr 06 14:29:25 2016 +0530<br>
@@ -1021,42 +1021,14 @@<br>
     }<br>
 }<br>
<br>
-void Entropy::codeSaoOffsetEO(int *offset, int typeIdx, int plane)<br>
+void Entropy::codeSaoTypeBits(int type, int plane)<br>
 {<br>
-    if (plane != 2)<br>
-    {<br>
-        encodeBin(1, m_contextState[OFF_SAO_TYPE_IDX_CTX]);<br>
-        encodeBinEP(1);<br>
-    }<br>
+    if (plane == 2)<br>
+        return;<br>
<br>
-    enum { OFFSET_THRESH = 1 << X265_MIN(X265_DEPTH - 5, 5) };<br>
-<br>
-    codeSaoMaxUvlc(offset[0], OFFSET_THRESH - 1);<br>
-    codeSaoMaxUvlc(offset[1], OFFSET_THRESH - 1);<br>
-    codeSaoMaxUvlc(-offset[2], OFFSET_THRESH - 1);<br>
-    codeSaoMaxUvlc(-offset[3], OFFSET_THRESH - 1);<br>
-    if (plane != 2)<br>
-        encodeBinsEP((uint32_t)(typeIdx), 2);<br>
-}<br>
-<br>
-void Entropy::codeSaoOffsetBO(int *offset, int bandPos, int plane)<br>
-{<br>
-    if (plane != 2)<br>
-    {<br>
-        encodeBin(1, m_contextState[OFF_SAO_TYPE_IDX_CTX]);<br>
-        encodeBinEP(0);<br>
-    }<br>
-<br>
-    enum { OFFSET_THRESH = 1 << X265_MIN(X265_DEPTH - 5, 5) };<br>
-<br>
-    for (int i = 0; i < SAO_NUM_OFFSET; i++)<br>
-        codeSaoMaxUvlc(abs(offset[i]), OFFSET_THRESH - 1);<br>
-<br>
-    for (int i = 0; i < SAO_NUM_OFFSET; i++)<br>
-        if (offset[i] != 0)<br>
-            encodeBinEP(offset[i] < 0);<br>
-<br>
-    encodeBinsEP(bandPos, 5);<br>
+    encodeBin(!!type, m_contextState[OFF_SAO_TYPE_IDX_CTX]);<br>
+    if (type)<br>
+        encodeBinEP(type - 1);<br>
 }<br>
<br>
 /** initialize context model with respect to QP and initialization value */<br>
diff -r 511241d3ee7d -r a747b7c2d77b source/encoder/entropy.h<br>
--- a/source/encoder/entropy.h  Wed Apr 06 13:12:17 2016 +0530<br>
+++ b/source/encoder/entropy.h  Wed Apr 06 14:29:25 2016 +0530<br>
@@ -169,7 +169,6 @@<br>
     void codeCoeffNxN(const CUData& cu, const coeff_t* coef, uint32_t absPartIdx, uint32_t log2TrSize, TextType ttype);<br>
<br>
     inline void codeSaoMerge(uint32_t code)                          { encodeBin(code, m_contextState[OFF_SAO_MERGE_FLAG_CTX]); }<br>
-    inline void codeSaoType(uint32_t code)                           { encodeBin(code, m_contextState[OFF_SAO_TYPE_IDX_CTX]); }<br>
     inline void codeMVPIdx(uint32_t symbol)                          { encodeBin(symbol, m_contextState[OFF_MVP_IDX_CTX]); }<br>
     inline void codeMergeFlag(const CUData& cu, uint32_t absPartIdx) { encodeBin(cu.m_mergeFlag[absPartIdx], m_contextState[OFF_MERGE_FLAG_EXT_CTX]); }<br>
     inline void codeSkipFlag(const CUData& cu, uint32_t absPartIdx)  { encodeBin(cu.isSkipped(absPartIdx), m_contextState[OFF_SKIP_FLAG_CTX + cu.getCtxSkipFlag(absPartIdx)]); }<br>
@@ -183,8 +182,7 @@<br>
     inline void codeTransformSkipFlags(uint32_t transformSkip, TextType ttype) { encodeBin(transformSkip, m_contextState[OFF_TRANSFORMSKIP_FLAG_CTX + (ttype ? NUM_TRANSFORMSKIP_FLAG_CTX : 0)]); }<br>
     void codeDeltaQP(const CUData& cu, uint32_t absPartIdx);<br>
     void codeSaoOffset(const SaoCtuParam& ctuParam, int plane);<br>
-    void codeSaoOffsetEO(int *offset, int typeIdx, int plane);<br>
-    void codeSaoOffsetBO(int *offset, int bandPos, int plane);<br>
+    void codeSaoTypeBits(int type, int plane);<br>
<br>
     /* RDO functions */<br>
     void estBit(EstBitsSbac& estBitsSbac, uint32_t log2TrSize, bool bIsLuma) const;<br>
diff -r 511241d3ee7d -r a747b7c2d77b source/encoder/sao.cpp<br>
--- a/source/encoder/sao.cpp    Wed Apr 06 13:12:17 2016 +0530<br>
+++ b/source/encoder/sao.cpp    Wed Apr 06 14:29:25 2016 +0530<br>
@@ -1247,13 +1247,11 @@<br>
         m_entropyCoder.codeSaoMerge(0);<br>
     m_entropyCoder.store(m_rdContexts.temp);<br>
<br>
-    int64_t mergeDist[NUM_MERGE_MODE] = { 0 };<br>
     int64_t bestCost = 0;<br>
-<br>
     // Estimate distortion and cost of new SAO params<br>
-    saoLumaComponentParamDist(saoParam, addr, mergeDist, lambda, bestCost);<br>
+    saoLumaComponentParamDist(saoParam, addr, lambda, bestCost);<br>
     if (chroma)<br>
-        saoChromaComponentParamDist(saoParam, addr, mergeDist, lambda, bestCost);<br>
+        saoChromaComponentParamDist(saoParam, addr, lambda, bestCost);<br>
<br>
     if (saoParam->bSaoFlag[0] || saoParam->bSaoFlag[1])<br>
     {<br>
@@ -1263,9 +1261,18 @@<br>
             if (!allowMerge[mergeIdx])<br>
                 continue;<br>
<br>
+            m_entropyCoder.load(m_rdContexts.cur);<br>
+            m_entropyCoder.resetBits();<br>
+            if (allowMerge[0])<br>
+                m_entropyCoder.codeSaoMerge(1 - mergeIdx);<br>
+            if (allowMerge[1] && (mergeIdx == 1))<br>
+                m_entropyCoder.codeSaoMerge(1);<br>
+<br>
+            int32_t rate = m_entropyCoder.getNumberOfWrittenBits();<br>
+<br>
+            int64_t estDist = 0;<br>
             for (int plane = 0; plane < 3; plane++)<br>
             {<br>
-                int64_t estDist = 0;<br>
                 SaoCtuParam* mergeSrcParam = &(saoParam->ctuParam[plane][addrMerge[mergeIdx]]);<br>
                 int typeIdx = mergeSrcParam->typeIdx;<br>
                 if (typeIdx >= 0)<br>
@@ -1277,18 +1284,11 @@<br>
                         estDist += estSaoDist(m_count[plane][typeIdx][classIdx + bandPos], mergeOffset, m_offsetOrg[plane][typeIdx][classIdx + bandPos]);<br>
                     }<br>
                 }<br>
-                mergeDist[mergeIdx + 1] += (estDist / (lambda[!!plane] >> 8));<br>
             }<br>
<br>
-            m_entropyCoder.load(m_rdContexts.cur);<br>
-            m_entropyCoder.resetBits();<br>
-            if (allowMerge[0])<br>
-                m_entropyCoder.codeSaoMerge(1 - mergeIdx);<br>
-            if (allowMerge[1] && (mergeIdx == 1))<br>
-                m_entropyCoder.codeSaoMerge(1);<br>
+            // Considering only luma lambda<br>
+            int64_t mergeCost = calcSaoRdoCost(estDist, rate, lambda[0]);<br>
<br>
-            uint32_t rate = m_entropyCoder.getNumberOfWrittenBits();<br>
-            int64_t mergeCost = mergeDist[mergeIdx + 1] + rate;<br>
             // Compare merge cost with best offset cost<br>
             if (mergeCost < bestCost)<br>
             {<br>
@@ -1372,7 +1372,7 @@<br>
     }<br>
 }<br>
<br>
-inline int64_t SAO::calcSaoRdoCost(int64_t distortion, uint32_t bits, int64_t lambda)<br>
+inline int64_t SAO::calcSaoRdoCost(int64_t distortion, int32_t bits, int64_t lambda)<br>
 {<br>
 #if X265_DEPTH < 10<br>
         X265_CHECK(bits <= (INT64_MAX - 128) / lambda,<br>
@@ -1386,10 +1386,9 @@<br>
         return distortion + ((bits * lambda + 128) >> 8);<br>
 }<br>
<br>
-void SAO::estIterOffset(int typeIdx, int64_t lambda, int32_t count, int32_t offsetOrg, int32_t& offset, int32_t& distClasses, int64_t& costClasses)<br>
+void SAO::estIterOffset(int32_t typeIdx, int64_t lambda, int32_t count, int32_t offsetOrg, int32_t& offset, int64_t& costClasses)<br>
 {<br>
     int bestOffset = 0;<br>
-    distClasses    = 0;<br>
<br>
     // Assuming sending quantized value 0 results in zero offset and sending the value zero needs 1 bit.<br>
     // entropy coder can be used to measure the exact rate here.<br>
@@ -1408,7 +1407,6 @@<br>
         {<br>
             bestCost = cost;<br>
             bestOffset = offset;<br>
-            distClasses = (int)dist;<br>
         }<br>
         offset = (offset > 0) ? (offset - 1) : (offset + 1);<br>
     }<br>
@@ -1417,49 +1415,48 @@<br>
     offset = bestOffset;<br>
 }<br>
<br>
-void SAO::saoLumaComponentParamDist(SAOParam* saoParam, int32_t addr, int64_t* mergeDist, int64_t* lambda, int64_t &bestCost)<br>
+void SAO::saoLumaComponentParamDist(SAOParam* saoParam, int32_t addr, int64_t* lambda, int64_t &bestCost)<br>
 {<br>
-    int64_t bestDist = 0;<br>
     int bestTypeIdx = -1;<br>
<br>
     SaoCtuParam* lclCtuParam = &saoParam->ctuParam[0][addr];<br>
-<br>
-    int32_t  distClasses[MAX_NUM_SAO_CLASS];<br>
     int64_t  costClasses[MAX_NUM_SAO_CLASS];<br>
<br>
     // RDO SAO_NA<br>
     m_entropyCoder.load(m_rdContexts.temp);<br>
     m_entropyCoder.resetBits();<br>
-    m_entropyCoder.codeSaoType(0);<br>
-    uint32_t rate = m_entropyCoder.getNumberOfWrittenBits();<br>
+    m_entropyCoder.codeSaoTypeBits(0, 0);<br>
+    int32_t rate = m_entropyCoder.getNumberOfWrittenBits();<br>
     int64_t costPartBest = calcSaoRdoCost(0, rate, lambda[0]);<br>
<br>
     // RDO SAO_EO<br>
+    m_entropyCoder.load(m_rdContexts.temp);<br>
+    m_entropyCoder.resetBits();<br>
+    m_entropyCoder.codeSaoTypeBits(2, 0); // sao_type_idx_ctx for EO = 2.<br>
+    int32_t eo_bits_type = m_entropyCoder.getNumberOfWrittenBits();<br>
+    int64_t eo_type_cost = calcSaoRdoCost(0, eo_bits_type, lambda[0]);<br>
+<br>
     for (int typeIdx = 0; typeIdx < MAX_NUM_SAO_TYPE - 1; typeIdx++)<br>
     {<br>
-        int64_t estDist = 0;<br>
+        int64_t cost = 0;<br>
         for (int classIdx = 1; classIdx < SAO_NUM_OFFSET + 1; classIdx++)<br>
         {<br>
             int32_t& count     = m_count[0][typeIdx][classIdx];<br>
             int32_t& offsetOrg = m_offsetOrg[0][typeIdx][classIdx];<br>
             int32_t& offsetOut = m_offset[0][typeIdx][classIdx];<br>
<br>
-            estIterOffset(typeIdx, lambda[0], count, offsetOrg, offsetOut, distClasses[classIdx], costClasses[classIdx]);<br>
+            estIterOffset(typeIdx, lambda[0], count, offsetOrg, offsetOut, costClasses[classIdx]);<br>
<br>
             //Calculate distortion<br>
-            estDist += distClasses[classIdx];<br>
+            cost += costClasses[classIdx];<br>
         }<br>
<br>
-        m_entropyCoder.load(m_rdContexts.temp);<br>
-        m_entropyCoder.resetBits();<br>
-        m_entropyCoder.codeSaoOffsetEO(m_offset[0][typeIdx] + 1, typeIdx, 0);<br>
-        uint32_t rate = m_entropyCoder.getNumberOfWrittenBits();<br>
-        int64_t cost = calcSaoRdoCost(estDist, rate, lambda[0]);<br>
+        // sao_eo_class_luma and sao_eo_class_chroma are fixed-length 2-bit syntax elements.<br>
+        cost += eo_type_cost + calcSaoRdoCost(0, 2, lambda[0]);<br>
<br>
         if (cost < costPartBest)<br>
         {<br>
             costPartBest = cost;<br>
-            bestDist = estDist;<br>
             bestTypeIdx = typeIdx;<br>
         }<br>
     }<br>
@@ -1474,14 +1471,19 @@<br>
     }<br>
<br>
     // RDO SAO_BO<br>
-    int64_t estDist = 0;<br>
+    m_entropyCoder.load(m_rdContexts.temp);<br>
+    m_entropyCoder.resetBits();<br>
+    m_entropyCoder.codeSaoTypeBits(1, 0); // sao_type_idx_ctx for BO = 1.<br>
+    int32_t bo_bits_type = m_entropyCoder.getNumberOfWrittenBits();<br>
+    int64_t bo_type_cost = calcSaoRdoCost(0, bo_bits_type, lambda[0]);<br>
+<br>
     for (int classIdx = 0; classIdx < MAX_NUM_SAO_CLASS; classIdx++)<br>
     {<br>
         int32_t&  count    = m_count[0][SAO_BO][classIdx];<br>
         int32_t& offsetOrg = m_offsetOrg[0][SAO_BO][classIdx];<br>
         int32_t& offsetOut = m_offset[0][SAO_BO][classIdx];<br>
<br>
-        estIterOffset(SAO_BO, lambda[0], count, offsetOrg, offsetOut, distClasses[classIdx], costClasses[classIdx]);<br>
+        estIterOffset(SAO_BO, lambda[0], count, offsetOrg, offsetOut, costClasses[classIdx]);<br>
     }<br>
<br>
     // Estimate Best Position<br>
@@ -1501,21 +1503,12 @@<br>
         }<br>
     }<br>
<br>
-    estDist = 0;<br>
-    for (int classIdx = bestClassBO; classIdx < bestClassBO + SAO_NUM_OFFSET; classIdx++)<br>
-        estDist += distClasses[classIdx];<br>
-<br>
-    // Estimate best BO cost<br>
-    m_entropyCoder.load(m_rdContexts.temp);<br>
-    m_entropyCoder.resetBits();<br>
-    m_entropyCoder.codeSaoOffsetBO(m_offset[0][SAO_BO] + bestClassBO, bestClassBO, 0);<br>
-    uint32_t estRate = m_entropyCoder.getNumberOfWrittenBits();<br>
-    int64_t cost = calcSaoRdoCost(estDist, estRate, lambda[0]);<br>
+    // sao_eo_class_luma and sao_bo_class_chroma are fixed-length 5-bit syntax elements.<br>
+    int64_t cost = bestRDCostBO + bo_type_cost + calcSaoRdoCost(0, 5, lambda[0]);<br>
<br>
     if (cost < costPartBest)<br>
     {<br>
         costPartBest = cost;<br>
-        bestDist = estDist;<br>
<br>
         lclCtuParam->mergeMode = SAO_MERGE_NONE;<br>
         lclCtuParam->typeIdx = SAO_BO;<br>
@@ -1524,38 +1517,39 @@<br>
             lclCtuParam->offset[classIdx] = m_offset[0][SAO_BO][classIdx + bestClassBO];<br>
     }<br>
<br>
-    mergeDist[0] = bestDist / (lambda[0] >> 8);<br>
-<br>
     m_entropyCoder.load(m_rdContexts.temp);<br>
     m_entropyCoder.codeSaoOffset(*lclCtuParam, 0);<br>
     m_entropyCoder.store(m_rdContexts.temp);<br>
<br>
-    uint32_t bits = m_entropyCoder.getNumberOfWrittenBits();<br>
-    bestCost = mergeDist[0] + bits;<br>
+    bestCost = costPartBest;<br>
 }<br>
<br>
-void SAO::saoChromaComponentParamDist(SAOParam* saoParam, int32_t addr, int64_t* mergeDist, int64_t* lambda, int64_t &bestCost)<br>
+void SAO::saoChromaComponentParamDist(SAOParam* saoParam, int32_t addr, int64_t* lambda, int64_t &bestCost)<br>
 {<br>
-    int64_t bestDist = 0;<br>
     int bestTypeIdx = -1;<br>
<br>
     SaoCtuParam* lclCtuParam[2] = { &saoParam->ctuParam[1][addr], &saoParam->ctuParam[2][addr] };<br>
<br>
     int64_t costClasses[MAX_NUM_SAO_CLASS];<br>
-    int32_t distClasses[MAX_NUM_SAO_CLASS];<br>
     int32_t bestClassBO[2] = { 0, 0 };<br>
<br>
     // RDO SAO_NA<br>
     m_entropyCoder.load(m_rdContexts.temp);<br>
     m_entropyCoder.resetBits();<br>
-    m_entropyCoder.codeSaoType(0);<br>
-    uint32_t bits = m_entropyCoder.getNumberOfWrittenBits();<br>
+    m_entropyCoder.codeSaoTypeBits(0, 0);<br>
+    int32_t bits = m_entropyCoder.getNumberOfWrittenBits();<br>
     int64_t costPartBest = calcSaoRdoCost(0, bits, lambda[1]);<br>
<br>
     // RDO SAO_EO<br>
+    m_entropyCoder.load(m_rdContexts.temp);<br>
+    m_entropyCoder.resetBits();<br>
+    m_entropyCoder.codeSaoTypeBits(2, 0); // sao_type_idx_ctx for EO = 2.<br>
+    int32_t eo_bits_type = m_entropyCoder.getNumberOfWrittenBits();<br>
+    int64_t eo_type_cost = calcSaoRdoCost(0, eo_bits_type, lambda[1]);<br>
+<br>
     for (int typeIdx = 0; typeIdx < MAX_NUM_SAO_TYPE - 1; typeIdx++)<br>
     {<br>
-        int64_t estDist[2] = {0, 0};<br>
+        int64_t cost = 0;<br>
         for (int compIdx = 1; compIdx < 3; compIdx++)<br>
         {<br>
             for (int classIdx = 1; classIdx < SAO_NUM_OFFSET + 1; classIdx++)<br>
@@ -1564,25 +1558,18 @@<br>
                 int32_t& offsetOrg = m_offsetOrg[compIdx][typeIdx][classIdx];<br>
                 int32_t& offsetOut = m_offset[compIdx][typeIdx][classIdx];<br>
<br>
-                estIterOffset(typeIdx, lambda[1], count, offsetOrg, offsetOut, distClasses[classIdx], costClasses[classIdx]);<br>
+                estIterOffset(typeIdx, lambda[1], count, offsetOrg, offsetOut, costClasses[classIdx]);<br>
<br>
-                estDist[compIdx - 1] += distClasses[classIdx];<br>
+                cost += costClasses[classIdx];<br>
             }<br>
         }<br>
<br>
-        m_entropyCoder.load(m_rdContexts.temp);<br>
-        m_entropyCoder.resetBits();<br>
-<br>
-        for (int compIdx = 0; compIdx < 2; compIdx++)<br>
-            m_entropyCoder.codeSaoOffsetEO(m_offset[compIdx + 1][typeIdx] + 1, typeIdx, compIdx + 1);<br>
-<br>
-        uint32_t estRate = m_entropyCoder.getNumberOfWrittenBits();<br>
-        int64_t cost = calcSaoRdoCost((estDist[0] + estDist[1]), estRate, lambda[1]);<br>
+        // sao_eo_class_luma and sao_eo_class_chroma are fixed-length 2-bit syntax elements.<br>
+        cost += eo_type_cost + calcSaoRdoCost(0, 2, lambda[1]);<br>
<br>
         if (cost < costPartBest)<br>
         {<br>
             costPartBest = cost;<br>
-            bestDist = (estDist[0] + estDist[1]);<br>
             bestTypeIdx = typeIdx;<br>
         }<br>
     }<br>
@@ -1600,8 +1587,13 @@<br>
     }<br>
<br>
     // RDO SAO_BO<br>
-    int64_t estDist[2];<br>
+    m_entropyCoder.load(m_rdContexts.temp);<br>
+    m_entropyCoder.resetBits();<br>
+    m_entropyCoder.codeSaoTypeBits(1, 0); // sao_type_idx_ctx for BO = 1.<br>
+    int32_t bo_bits_type = m_entropyCoder.getNumberOfWrittenBits();<br>
+    int64_t bo_type_cost = calcSaoRdoCost(0, bo_bits_type, lambda[1]);<br>
<br>
+    int64_t cost = 0;<br>
     // Estimate Best Position<br>
     for (int compIdx = 1; compIdx < 3; compIdx++)<br>
     {<br>
@@ -1613,7 +1605,7 @@<br>
             int32_t& offsetOrg = m_offsetOrg[compIdx][SAO_BO][classIdx];<br>
             int32_t& offsetOut = m_offset[compIdx][SAO_BO][classIdx];<br>
<br>
-            estIterOffset(SAO_BO, lambda[1], count, offsetOrg, offsetOut, distClasses[classIdx], costClasses[classIdx]);<br>
+            estIterOffset(SAO_BO, lambda[1], count, offsetOrg, offsetOut, costClasses[classIdx]);<br>
         }<br>
<br>
         for (int i = 0; i < MAX_NUM_SAO_CLASS - SAO_NUM_OFFSET + 1; i++)<br>
@@ -1629,24 +1621,15 @@<br>
             }<br>
         }<br>
<br>
-        estDist[compIdx - 1] = 0;<br>
-        for (int classIdx = bestClassBO[compIdx - 1]; classIdx < bestClassBO[compIdx - 1] + SAO_NUM_OFFSET; classIdx++)<br>
-            estDist[compIdx - 1] += distClasses[classIdx];<br>
+        cost += bestRDCostBO;<br>
     }<br>
<br>
-    m_entropyCoder.load(m_rdContexts.temp);<br>
-    m_entropyCoder.resetBits();<br>
-<br>
-    for (int compIdx = 0; compIdx < 2; compIdx++)<br>
-        m_entropyCoder.codeSaoOffsetBO(m_offset[compIdx + 1][SAO_BO] + bestClassBO[compIdx], bestClassBO[compIdx], compIdx + 1);<br>
-<br>
-    uint32_t estRate = m_entropyCoder.getNumberOfWrittenBits();<br>
-    int64_t cost = calcSaoRdoCost((estDist[0] + estDist[1]), estRate, lambda[1]);<br>
+    // sao_eo_class_luma and sao_bo_class_chroma are fixed-length 5-bit syntax elements.<br>
+    cost += bo_type_cost + calcSaoRdoCost(0, 5, lambda[1]);<br>
<br>
     if (cost < costPartBest)<br>
     {<br>
         costPartBest = cost;<br>
-        bestDist = (estDist[0] + estDist[1]);<br>
<br>
         for (int compIdx = 0; compIdx < 2; compIdx++)<br>
         {<br>
@@ -1658,15 +1641,12 @@<br>
         }<br>
     }<br>
<br>
-    mergeDist[0] += (bestDist / (lambda[1] >> 8));<br>
     m_entropyCoder.load(m_rdContexts.temp);<br>
-<br>
     m_entropyCoder.codeSaoOffset(*lclCtuParam[0], 1);<br>
     m_entropyCoder.codeSaoOffset(*lclCtuParam[1], 2);<br>
     m_entropyCoder.store(m_rdContexts.temp);<br>
<br>
-    uint32_t rate = m_entropyCoder.getNumberOfWrittenBits();<br>
-    bestCost = mergeDist[0] + rate;<br>
+    bestCost += costPartBest;<br>
 }<br>
<br>
 // NOTE: must put in namespace X265_NS since we need class SAO<br>
diff -r 511241d3ee7d -r a747b7c2d77b source/encoder/sao.h<br>
--- a/source/encoder/sao.h      Wed Apr 06 13:12:17 2016 +0530<br>
+++ b/source/encoder/sao.h      Wed Apr 06 14:29:25 2016 +0530<br>
@@ -54,7 +54,6 @@<br>
     enum { OFFSET_THRESH = 1 << X265_MIN(X265_DEPTH - 5, 5) };<br>
     enum { NUM_EDGETYPE = 5 };<br>
     enum { NUM_PLANE = 3 };<br>
-    enum { NUM_MERGE_MODE = 3 };<br>
     enum { SAO_DEPTHRATE_SIZE = 4 };<br>
<br>
     static const uint32_t s_eoTable[NUM_EDGETYPE];<br>
@@ -127,14 +126,13 @@<br>
     void calcSaoStatsCu(int addr, int plane);<br>
     void calcSaoStatsCu_BeforeDblk(Frame* pic, int idxX, int idxY);<br>
<br>
-    void saoLumaComponentParamDist(SAOParam* saoParam, int addr, int64_t* mergeDist, int64_t* lambda, int64_t &bestCost);<br>
-    void saoChromaComponentParamDist(SAOParam* saoParam, int addr, int64_t* mergeDist, int64_t* lambda, int64_t &bestCost);<br>
+    void saoLumaComponentParamDist(SAOParam* saoParam, int addr, int64_t* lambda, int64_t &bestCost);<br>
+    void saoChromaComponentParamDist(SAOParam* saoParam, int addr, int64_t* lambda, int64_t &bestCost);<br>
<br>
-    void estIterOffset(int typeIdx, int64_t lambda, int32_t count, int32_t offsetOrg, int32_t& offset,<br>
-                          int32_t& distClasses, int64_t& costClasses);<br>
+    void estIterOffset(int32_t typeIdx, int64_t lambda, int32_t count, int32_t offsetOrg, int32_t& offset, int64_t& costClasses);<br>
     void rdoSaoUnitRowEnd(const SAOParam* saoParam, int numctus);<br>
     void rdoSaoUnitCu(SAOParam* saoParam, int rowBaseAddr, int idxX, int addr);<br>
-    int64_t calcSaoRdoCost(int64_t distortion, uint32_t bits, int64_t lambda);<br>
+    int64_t calcSaoRdoCost(int64_t distortion, int32_t bits, int64_t lambda);<br>
<br>
     void saoStatsInitialOffset(bool chroma);<br>
<br>
</blockquote></div><br></div>