shithub: openh264

Download patch

ref: b7c54242a9161b7d0e2acf024c5e215a816c1e18
parent: 04c2a7ac5cf4a6ad23ac8e1d89427425e05085d9
author: Haibo Zhu <[email protected]>
date: Tue Sep 16 19:23:31 EDT 2014

Modify the random value range which only for debug
Modify the CLIP3 macros to match the CLIP3(value, min, max)
Add partly UT cases into it which related to function call
Modify the typo for WELS_NEON
Add partly UT cases into it which related to Bs calculation
Update Windows VS project

--- a/test/build/win32/codec_ut/codec_unittest.vcproj
+++ b/test/build/win32/codec_ut/codec_unittest.vcproj
@@ -471,6 +471,10 @@
 				</FileConfiguration>
 			</File>
 			<File
+				RelativePath="..\..\..\decoder\DecUT_DeblockCommon.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\..\decoder\DecUT_DecExt.cpp"
 				>
 				<FileConfiguration
--- a/test/decoder/DecUT_DeblockCommon.cpp
+++ b/test/decoder/DecUT_DeblockCommon.cpp
@@ -3,7 +3,7 @@
 #include "../../codec/decoder/core/inc/deblocking.h"
 #include "../../codec/common/inc/deblocking_common.h"
 
-#define CLIP3(VALUE, MIN, MAX) ((VALUE) < (MIN) ? (MIN) : ((VALUE) > (MAX) ? (MAX) : (VALUE)))
+#define CLIP3(MIN, MAX, VALUE) ((VALUE) < (MIN) ? (MIN) : ((VALUE) > (MAX) ? (MAX) : (VALUE)))
 
 using namespace WelsDec;
 
@@ -15,6 +15,10 @@
                                 int32_t iBeta, int8_t* pTc);
 extern void DeblockChromaEq4_c (uint8_t* pPixCb, uint8_t* pPixCr, int32_t iStrideX, int32_t iStrideY, int32_t iAlpha,
                                 int32_t iBeta);
+namespace WelsDec {
+extern void FilteringEdgeChromaHV (PDqLayer pCurDqLayer, PDeblockingFilter  pFilter, int32_t iBoundryFlag);
+extern void FilteringEdgeLumaHV (PDqLayer pCurDqLayer, PDeblockingFilter  pFilter, int32_t iBoundryFlag);
+}
 
 /* Macros body */
 #define GENERATE_DATA_DEBLOCKING(pBase, pRef, iWidth) \
@@ -24,7 +28,7 @@
   iTc[0] = iTc[1] = iTc[2] = iTc[3] = 25; \
   pBase[0] = pRef[0] = 128; \
   for (int i = 1; i < iWidth*iWidth; i++) { \
-  pBase[i] = pRef[i] = CLIP3( pBase[i-1] -16 + rand()%32, 0, 255); \
+  pBase[i] = pRef[i] = CLIP3( 0, 255, pBase[i-1] -16 + rand()%32 ); \
   } \
 } else if (iNum==1) { \
   iAlpha = 4; \
@@ -32,7 +36,7 @@
   iTc[0] = iTc[1] = iTc[2] = iTc[3] = 9; \
   pBase[0] = pRef[0] = 128; \
   for (int i = 1; i < iWidth*iWidth; i++) { \
-  pBase[i] = pRef[i] = CLIP3( pBase[i-1] -4 + rand()%8, 0, 255); \
+  pBase[i] = pRef[i] = CLIP3( 0, 255, pBase[i-1] -4 + rand()%8); \
   } \
 } else { \
   iAlpha = rand() % 256; \
@@ -41,10 +45,50 @@
   iTc[i] = rand() % 26; \
   } \
   for (int i = 0; i < iWidth*iWidth; i++) { \
-  pBase[i] = pRef[i] = rand() % 200; \
+  pBase[i] = pRef[i] = rand() % 256; \
   } \
 }
 
+/* NULL functions, for null call */
+void UT_DeblockingFuncInterface (PDqLayer pCurDqLayer, PDeblockingFilter  filter, int32_t boundry_flag) {
+  return;
+}
+
+/* Set deblocking functions to NULL */
+void UT_DeblockingFuncLumaLT4Func (uint8_t* iSampleY, int32_t iStride, int32_t iAlpha, int32_t iBeta, int8_t* iTc) {
+  if (iAlpha > 0 || iBeta > 0) {
+    iSampleY[0]++;
+  }
+  return;
+}
+
+void UT_DeblockingFuncLumaEQ4Func (uint8_t* iSampleY, int32_t iStride, int32_t iAlpha, int32_t iBeta) {
+  if (iAlpha > 0 || iBeta > 0) {
+    iSampleY[0]++;
+  }
+  return;
+}
+
+void UT_DeblockingFuncChromaLT4Func (uint8_t* iSampleCb, uint8_t* iSampleCr, int32_t iStride, int32_t iAlpha,
+                                     int32_t iBeta, int8_t* iTc) {
+  if (iAlpha > 0 || iBeta > 0) {
+    iSampleCb[0]++;
+    iSampleCr[0]++;
+  }
+  return;
+}
+
+void UT_DeblockingFuncChromaEQ4Func (uint8_t* iSampleCb, uint8_t* iSampleCr, int32_t iStride, int32_t iAlpha,
+                                     int32_t iBeta) {
+  if (iAlpha > 0 || iBeta > 0) {
+    iSampleCb[0]++;
+    iSampleCr[0]++;
+  }
+  return;
+}
+
+/* Public function for local test */
+
 /* Anchor functions body, some directly from the current code */
 void anchor_DeblockingLumaNormal (uint8_t* pPix, int32_t iStrideX, int32_t iStrideY, int32_t iAlpha, int32_t iBeta,
                                   int8_t* pTc) {
@@ -69,20 +113,20 @@
     if (abs (p[0] - q[0]) < iAlpha && abs (p[1] - p[0]) < iBeta && abs (q[1] - q[0]) < iBeta) {
       // 8-470
       if (abs (p[2] - p[0]) < iBeta) {
-        pPix[iStrideX * -2] = CLIP3 (p[1] + CLIP3 (((p[2] + ((p[0] + q[0] + 1) >> 1) - (p[1] << 1)) >> 1), -1 * pTc[iIndexTc],
-                                     pTc[iIndexTc]), 0, 255);
+        pPix[iStrideX * -2] = CLIP3 (0, 255, p[1] + CLIP3 (-1 * pTc[iIndexTc], pTc[iIndexTc],
+                                     ((p[2] + ((p[0] + q[0] + 1) >> 1) - (p[1] << 1)) >> 1)));
         iTc++;
       }
       // 8-472
       if (abs (q[2] - q[0]) < iBeta) {
-        pPix[iStrideX * 1] = CLIP3 (q[1] + CLIP3 (((q[2] + ((p[0] + q[0] + 1) >> 1) - (q[1] << 1)) >> 1), -1 * pTc[iIndexTc],
-                                    pTc[iIndexTc]), 0, 255);
+        pPix[iStrideX * 1] = CLIP3 (0, 255, q[1] + CLIP3 (-1 * pTc[iIndexTc],  pTc[iIndexTc],
+                                    ((q[2] + ((p[0] + q[0] + 1) >> 1) - (q[1] << 1)) >> 1)));
         iTc++;
       }
       // 8-467,468,469
-      iDelta = CLIP3 (((((q[0] - p[0]) << 2) + (p[1] - q[1]) + 4) >> 3), -1 * iTc, iTc);
-      pPix[iStrideX * -1] = CLIP3 ((p[0] + iDelta), 0, 255);
-      pPix[0] = CLIP3 ((q[0] - iDelta), 0, 255);
+      iDelta = CLIP3 (-1 * iTc, iTc, ((((q[0] - p[0]) << 2) + (p[1] - q[1]) + 4) >> 3));
+      pPix[iStrideX * -1] = CLIP3 (0, 255, (p[0] + iDelta));
+      pPix[0] = CLIP3 (0, 255, (q[0] - iDelta));
     }
 
     // Next line
@@ -152,9 +196,9 @@
     // filterSampleFlag, 8-460
     if (abs (p[0] - q[0]) < iAlpha && abs (p[1] - p[0]) < iBeta && abs (q[1] - q[0]) < iBeta) {
       // 8-467, 468, 469
-      iDelta = CLIP3 (((((q[0] - p[0]) << 2) + (p[1] - q[1]) + 4) >> 3), -1 * iTc, iTc);
-      pPixCb[iStrideX * -1] = CLIP3 ((p[0] + iDelta), 0, 255);
-      pPixCb[iStrideX * 0 ] = CLIP3 ((q[0] - iDelta), 0, 255);
+      iDelta = CLIP3 (-1 * iTc, iTc, ((((q[0] - p[0]) << 2) + (p[1] - q[1]) + 4) >> 3));
+      pPixCb[iStrideX * -1] = CLIP3 (0, 255, (p[0] + iDelta));
+      pPixCb[iStrideX * 0 ] = CLIP3 (0, 255, (q[0] - iDelta));
     }
     pPixCb += iStrideY;
 
@@ -167,9 +211,9 @@
     // filterSampleFlag, 8-460
     if (abs (p[0] - q[0]) < iAlpha && abs (p[1] - p[0]) < iBeta && abs (q[1] - q[0]) < iBeta) {
       // 8-467, 468, 469
-      iDelta = CLIP3 (((((q[0] - p[0]) << 2) + (p[1] - q[1]) + 4) >> 3), -1 * iTc, iTc);
-      pPixCr[iStrideX * -1] = CLIP3 ((p[0] + iDelta), 0, 255);
-      pPixCr[iStrideX * 0 ] = CLIP3 ((q[0] - iDelta), 0, 255);
+      iDelta = CLIP3 (-1 * iTc, iTc, ((((q[0] - p[0]) << 2) + (p[1] - q[1]) + 4) >> 3));
+      pPixCr[iStrideX * -1] = CLIP3 (0, 255, (p[0] + iDelta));
+      pPixCr[iStrideX * 0 ] = CLIP3 (0, 255, (q[0] - iDelta));
     }
     pPixCr += iStrideY;
   }
@@ -191,8 +235,8 @@
     // filterSampleFlag, 8-460
     if (abs (p[0] - q[0]) < iAlpha && abs (p[1] - p[0]) < iBeta && abs (q[1] - q[0]) < iBeta) {
       // 8-480, 487
-      pPixCb[iStrideX * -1] = CLIP3 ((2 * p[1] + p[0] + q[1] + 2) >> 2, 0, 255);
-      pPixCb[iStrideX * 0 ] = CLIP3 ((2 * q[1] + q[0] + p[1] + 2) >> 2, 0, 255);
+      pPixCb[iStrideX * -1] = CLIP3 (0, 255, (2 * p[1] + p[0] + q[1] + 2) >> 2);
+      pPixCb[iStrideX * 0 ] = CLIP3 (0, 255, (2 * q[1] + q[0] + p[1] + 2) >> 2);
     }
     pPixCb += iStrideY;
 
@@ -205,8 +249,8 @@
     // filterSampleFlag, 8-460
     if (abs (p[0] - q[0]) < iAlpha && abs (p[1] - p[0]) < iBeta && abs (q[1] - q[0]) < iBeta) {
       // 8-480, 487
-      pPixCr[iStrideX * -1] = CLIP3 ((2 * p[1] + p[0] + q[1] + 2) >> 2, 0, 255);
-      pPixCr[iStrideX * 0 ] = CLIP3 ((2 * q[1] + q[0] + p[1] + 2) >> 2, 0, 255);
+      pPixCr[iStrideX * -1] = CLIP3 (0, 255, (2 * p[1] + p[0] + q[1] + 2) >> 2);
+      pPixCr[iStrideX * 0 ] = CLIP3 (0, 255, (2 * q[1] + q[0] + p[1] + 2) >> 2);
     }
     pPixCr += iStrideY;
   }
@@ -369,4 +413,542 @@
                         (uint32_t)iPixCrRef[i] << ")";
     }
   }
+}
+
+/////////// Logic call functions
+TEST (DecoderDeblocking, DeblockingAvailableNoInterlayer) {
+  // DeblockingAvailableNoInterlayer (PDqLayer pCurDqLayer, int32_t iFilterIdc)
+  SDqLayer sLayer;
+  int32_t iFilterIdc;
+  int32_t iSliceIdc[9];
+
+  sLayer.pSliceIdc = iSliceIdc;
+
+  /* iFilterIdc only support 0 and 2, which is related with the encode configuration */
+  /* Using 3x3 grids to simulate the different situations */
+
+#define UT_DBAvailable_idc_0(iX, iY, iExpect) \
+  iFilterIdc = 0; \
+  sLayer.iMbX = iX; \
+  sLayer.iMbY = iY; \
+  sLayer.iMbXyIndex =  sLayer.iMbX + sLayer.iMbY*3; \
+  sLayer.iMbWidth = 3; \
+  EXPECT_TRUE(DeblockingAvailableNoInterlayer (&sLayer, iFilterIdc)==iExpect);
+
+#define UT_DBAvailable_idc_2_same_slice(iX, iY, iExpect) \
+  iFilterIdc = 2; \
+  sLayer.iMbX = iX; \
+  sLayer.iMbY = iY; \
+  sLayer.iMbXyIndex =  sLayer.iMbX + sLayer.iMbY*3; \
+  sLayer.iMbWidth = 3; \
+  iSliceIdc[0] = rand()%10; \
+  for (int i=1; i<9; i++) { \
+    iSliceIdc[i] = iSliceIdc[0]; \
+  } \
+  EXPECT_TRUE(DeblockingAvailableNoInterlayer (&sLayer, iFilterIdc)==iExpect)<<"Same Slice";
+
+#define UT_DBAvailable_idc_2_diff_slice(iX, iY, iExpect) \
+  iFilterIdc = 2; \
+  sLayer.iMbX = iX; \
+  sLayer.iMbY = iY; \
+  sLayer.iMbXyIndex =  sLayer.iMbX + sLayer.iMbY*3; \
+  sLayer.iMbWidth = 3; \
+  for (int i=0; i<9; i++) { \
+    iSliceIdc[i] = i; \
+  } \
+  EXPECT_TRUE(DeblockingAvailableNoInterlayer (&sLayer, iFilterIdc)==iExpect)<<"Different Slice";
+
+  // (1) idc==0
+  UT_DBAvailable_idc_0 (0, 0, 0x00)
+  UT_DBAvailable_idc_0 (0, 1, 0x02)
+  UT_DBAvailable_idc_0 (0, 2, 0x02)
+  UT_DBAvailable_idc_0 (1, 0, 0x01)
+  UT_DBAvailable_idc_0 (1, 1, 0x03)
+  UT_DBAvailable_idc_0 (1, 2, 0x03)
+  UT_DBAvailable_idc_0 (2, 0, 0x01)
+  UT_DBAvailable_idc_0 (2, 1, 0x03)
+  UT_DBAvailable_idc_0 (2, 2, 0x03)
+
+  // (2) idc==2, same slice
+  UT_DBAvailable_idc_2_same_slice (0, 0, 0x00)
+  UT_DBAvailable_idc_2_same_slice (0, 1, 0x02)
+  UT_DBAvailable_idc_2_same_slice (0, 2, 0x02)
+  UT_DBAvailable_idc_2_same_slice (1, 0, 0x01)
+  UT_DBAvailable_idc_2_same_slice (1, 1, 0x03)
+  UT_DBAvailable_idc_2_same_slice (1, 2, 0x03)
+  UT_DBAvailable_idc_2_same_slice (2, 0, 0x01)
+  UT_DBAvailable_idc_2_same_slice (2, 1, 0x03)
+  UT_DBAvailable_idc_2_same_slice (2, 2, 0x03)
+
+  // (3) idc==3, diff slice
+  UT_DBAvailable_idc_2_diff_slice (0, 0, 0x00)
+  UT_DBAvailable_idc_2_diff_slice (0, 1, 0x00)
+  UT_DBAvailable_idc_2_diff_slice (0, 2, 0x00)
+  UT_DBAvailable_idc_2_diff_slice (1, 0, 0x00)
+  UT_DBAvailable_idc_2_diff_slice (1, 1, 0x00)
+  UT_DBAvailable_idc_2_diff_slice (1, 2, 0x00)
+  UT_DBAvailable_idc_2_diff_slice (2, 0, 0x00)
+  UT_DBAvailable_idc_2_diff_slice (2, 1, 0x00)
+  UT_DBAvailable_idc_2_diff_slice (2, 2, 0x00)
+}
+
+TEST (DecoderDeblocking, DeblockingInit) {
+  // void  DeblockingInit (PDeblockingFunc pDeblockingFunc,  int32_t iCpu)
+  SDeblockingFunc sDBFunc;
+  memset (&sDBFunc, 0, sizeof (SDeblockingFunc));
+
+#define DB_FUNC_CPUFLAG(idx) \
+  EXPECT_TRUE(sDBFunc.pfLumaDeblockingLT4Ver == &DeblockLumaLt4V_##idx); \
+  EXPECT_TRUE(sDBFunc.pfLumaDeblockingEQ4Ver == &DeblockLumaEq4V_##idx); \
+  EXPECT_TRUE(sDBFunc.pfLumaDeblockingLT4Hor == &DeblockLumaLt4H_##idx); \
+  EXPECT_TRUE(sDBFunc.pfLumaDeblockingEQ4Hor == &DeblockLumaEq4H_##idx); \
+  EXPECT_TRUE(sDBFunc.pfChromaDeblockingLT4Ver == &DeblockChromaLt4V_##idx); \
+  EXPECT_TRUE(sDBFunc.pfChromaDeblockingEQ4Ver == &DeblockChromaEq4V_##idx); \
+  EXPECT_TRUE(sDBFunc.pfChromaDeblockingLT4Hor == &DeblockChromaLt4H_##idx); \
+  EXPECT_TRUE(sDBFunc.pfChromaDeblockingEQ4Hor == &DeblockChromaEq4H_##idx);
+
+#ifndef X86_ASM
+  // pure C
+  DeblockingInit (&sDBFunc, 0x00000000);
+  DB_FUNC_CPUFLAG (c)
+#endif
+
+#ifdef X86_ASM
+  // pure C
+  DeblockingInit (&sDBFunc, 0x00000000);
+  DB_FUNC_CPUFLAG (c)
+
+  // SSE3
+  DeblockingInit (&sDBFunc, 0x00000200);
+  DB_FUNC_CPUFLAG (ssse3)
+#endif
+
+#ifdef HAVE_NEON
+  // pure C
+  DeblockingInit (&sDBFunc, 0x00000000);
+  DB_FUNC_CPUFLAG (c)
+
+  // NEON
+  DeblockingInit (&sDBFunc, 0x000004);
+  DB_FUNC_CPUFLAG (neon)
+#endif
+
+#ifdef HAVE_NEON_AARCH64
+  // pure C
+  DeblockingInit (&sDBFunc, 0x00000000);
+  DB_FUNC_CPUFLAG (c)
+
+  // NEON_AARCH64
+  DeblockingInit (&sDBFunc, 0x000004);
+  DB_FUNC_CPUFLAG (AArch64_neon)
+#endif
+}
+
+TEST (DecoderDeblocking, WelsDeblockingFilterSlice) {
+  // void WelsDeblockingFilterSlice (PWelsDecoderContext pCtx, PDeblockingFilterMbFunc pDeblockMb)
+
+  /* NOT support FMO now */
+  SWelsDecoderContext sCtx;
+  SDqLayer sDqLayer;
+  SSps sSPS;
+  SPps sPPS;
+  SPicture sDec;
+  PDeblockingFilterMbFunc pDeblockMb = &UT_DeblockingFuncInterface;
+
+  /* NOT do actual deblocking process, set related parameters to null */
+  sCtx.pDec = &sDec;
+  sCtx.pDec->iLinesize[0] = sCtx.pDec->iLinesize[1] = sCtx.pDec->iLinesize[2] = 0;
+  sCtx.pDec->pData[0] = sCtx.pDec->pData[1] = sCtx.pDec->pData[2] = NULL;
+
+  /* As no FMO in encoder now, the multi slicegroups has not been set */
+  sCtx.pFmo = NULL;
+
+  sCtx.pCurDqLayer = &sDqLayer;
+  /* As void return, using iMbXyIndex to reflect whether the all MBs have been passed. */
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.iFirstMbInSlice = 0;
+  sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice = 0;
+
+  // whether disable Deblocking Filter Idc
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.uiDisableDeblockingFilterIdc = 0;
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.iSliceAlphaC0Offset = 0;
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.iSliceBetaOffset = 0;
+
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pSps = &sSPS;
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pSps->uiTotalMbCount = 0;
+
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pPps = &sPPS;
+  /* Only test one slicegroup, not reflect the FMO func */
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pPps->uiNumSliceGroups = 1;
+
+  // (1) Normal case, the iTotalMbInCurSlice == pSps->uiTotalMbCount
+  sDqLayer.iMbX = sDqLayer.iMbY = 0;
+  sDqLayer.iMbXyIndex = 0;
+  sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice = rand() % 256;
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pSps->uiTotalMbCount =
+    sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice;
+  sDqLayer.iMbWidth = 1 + rand() % 128;
+  WelsDeblockingFilterSlice (&sCtx, pDeblockMb);
+  EXPECT_TRUE ((sDqLayer.iMbXyIndex + 1) == sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice) << sDqLayer.iMbXyIndex
+      << " " << sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice;
+
+  // (2) Normal case, multi slices, iTotalMbInCurSlice < pSps->uiTotalMbCount
+  sDqLayer.iMbX = sDqLayer.iMbY = 0;
+  sDqLayer.iMbXyIndex = 0;
+  sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice = rand() % 256;
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pSps->uiTotalMbCount =
+    sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice + rand() % 256;
+  sDqLayer.iMbWidth = 1 + rand() % 128;
+  WelsDeblockingFilterSlice (&sCtx, pDeblockMb);
+  EXPECT_TRUE ((sDqLayer.iMbXyIndex + 1) == sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice);
+
+  // (3) Special case, iTotalMbInCurSlice > pSps->uiTotalMbCount, JUST FOR TEST
+  sDqLayer.iMbX = sDqLayer.iMbY = 0;
+  sDqLayer.iMbXyIndex = 0;
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pSps->uiTotalMbCount = rand() % 256;
+  sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice =
+    sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pSps->uiTotalMbCount + rand() % 256;
+  sDqLayer.iMbWidth = 1 + rand() % 128;
+  WelsDeblockingFilterSlice (&sCtx, pDeblockMb);
+  EXPECT_TRUE ((sDqLayer.iMbXyIndex + 1) ==
+               sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pSps->uiTotalMbCount);
+
+  // (4) Special case, uiDisableDeblockingFilterIdc==1, disable deblocking
+  sDqLayer.iMbX = sDqLayer.iMbY = 0;
+  sDqLayer.iMbXyIndex = 0;
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.uiDisableDeblockingFilterIdc = 1;
+  sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice = rand() % 256;
+  sDqLayer.sLayerInfo.sSliceInLayer.sSliceHeaderExt.sSliceHeader.pSps->uiTotalMbCount =
+    sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice;
+  sDqLayer.iMbWidth = 1 + rand() % 128;
+  WelsDeblockingFilterSlice (&sCtx, pDeblockMb);
+  EXPECT_TRUE (sDqLayer.iMbXyIndex == 0) << sDqLayer.iMbXyIndex << " " <<
+                                         sDqLayer.sLayerInfo.sSliceInLayer.iTotalMbInCurSlice;
+
+}
+
+TEST (DecoderDeblocking, FilteringEdgeChromaHV) {
+  // void FilteringEdgeChromaHV (PDqLayer pCurDqLayer, PDeblockingFilter  pFilter, int32_t iBoundryFlag)
+  SDqLayer sDqLayer;
+  SDeblockingFilter sFilter;
+  int32_t iBoundryFlag = 0x01;
+  int32_t iQP;
+
+  memset (&sDqLayer, 0, sizeof (SDqLayer));
+  memset (&sFilter, 0, sizeof (SDeblockingFilter));
+
+  SDeblockingFunc sDBFunc;
+  sFilter.pLoopf = &sDBFunc;
+  sFilter.pLoopf->pfChromaDeblockingLT4Hor = &UT_DeblockingFuncChromaLT4Func;
+  sFilter.pLoopf->pfChromaDeblockingLT4Ver = &UT_DeblockingFuncChromaLT4Func;
+  sFilter.pLoopf->pfChromaDeblockingEQ4Hor = &UT_DeblockingFuncChromaEQ4Func;
+  sFilter.pLoopf->pfChromaDeblockingEQ4Ver = &UT_DeblockingFuncChromaEQ4Func;
+
+  int8_t iChromaQP[9];
+  sDqLayer.pChromaQp = iChromaQP;
+
+  uint8_t iCb[9] = {0};
+  uint8_t iCr[9] = {0};
+  sFilter.pCsData[1] = iCb;
+  sFilter.pCsData[2] = iCr;
+  sFilter.iCsStride[0] = sFilter.iCsStride[1] = 2;
+
+  sDqLayer.iMbX = 0;
+  sDqLayer.iMbY = 0; //Only for test easy
+  sDqLayer.iMbXyIndex = 1;  // this function has NO iMbXyIndex validation
+
+#define UT_DB_CHROMA_TEST(iFlag, iQP, iV0, iV1, iV2) \
+  iBoundryFlag = iFlag; \
+  memset(iChromaQP, iQP, sizeof(int8_t)*9); \
+  memset(iCb, 0, sizeof(uint8_t)*9); \
+  memset(iCr, 0, sizeof(uint8_t)*9); \
+  FilteringEdgeChromaHV(&sDqLayer, &sFilter, iBoundryFlag); \
+  EXPECT_TRUE(iCb[0]==iV0 && iCr[0]==iV0); \
+  EXPECT_TRUE(iCb[2<<1]==iV1 && iCr[2<<1]==iV1); \
+  EXPECT_TRUE(iCb[(2<<1)*sFilter.iCsStride[1]]==iV2 && iCr[(2<<1)*sFilter.iCsStride[1]]==iV2);
+
+  // QP<=15, iAlpha == iBeta == 0, TOP & LEFT
+  iQP = rand() % 16;
+  UT_DB_CHROMA_TEST (0x03, iQP, 0, 0, 0)
+
+  // QP>=16, iAlpha>0 && iBeta>0, TOP & LEFT
+  iQP = 16 + rand() % 35;
+  UT_DB_CHROMA_TEST (0x03, iQP, 2, 1, 1)
+
+  // QP<=15, iAlpha == iBeta == 0, TOP | LEFT
+  iQP = rand() % 16;
+  UT_DB_CHROMA_TEST (0x01, iQP, 0, 0, 0)
+  iQP = rand() % 16;
+  UT_DB_CHROMA_TEST (0x02, iQP, 0, 0, 0)
+
+  // QP>=16, iAlpha>0 && iBeta>0, TOP | LEFT
+  iQP = 16 + rand() % 35;
+  UT_DB_CHROMA_TEST (0x01, iQP, 1, 1, 1)
+  iQP = 16 + rand() % 35;
+  UT_DB_CHROMA_TEST (0x02, iQP, 1, 1, 1)
+
+  // QP<=15, iAlpha == iBeta == 0, !TOP & !LEFT
+  iQP = rand() % 16;
+  UT_DB_CHROMA_TEST (0x00, iQP, 0, 0, 0)
+
+  // QP>=16, iAlpha>0 && iBeta>0, !TOP & !LEFT
+  iQP = 16 + rand() % 35;
+  UT_DB_CHROMA_TEST (0x00, iQP, 0, 1, 1)
+}
+
+TEST (DecoderDeblocking, FilteringEdgeLumaHV) {
+  // void FilteringEdgeLumaHV (PDqLayer pCurDqLayer, PDeblockingFilter  pFilter, int32_t iBoundryFlag)
+  SDqLayer sDqLayer;
+  SDeblockingFilter sFilter;
+  int32_t iBoundryFlag = 0x03;
+  int32_t iQP;
+
+  memset (&sDqLayer, 0, sizeof (SDqLayer));
+  memset (&sFilter, 0, sizeof (SDeblockingFilter));
+
+  SDeblockingFunc sDBFunc;
+  sFilter.pLoopf = &sDBFunc;
+  sFilter.pLoopf->pfLumaDeblockingLT4Hor = &UT_DeblockingFuncLumaLT4Func;
+  sFilter.pLoopf->pfLumaDeblockingEQ4Hor = &UT_DeblockingFuncLumaEQ4Func;
+  sFilter.pLoopf->pfLumaDeblockingLT4Ver = &UT_DeblockingFuncLumaLT4Func;
+  sFilter.pLoopf->pfLumaDeblockingEQ4Ver = &UT_DeblockingFuncLumaEQ4Func;
+
+  int8_t iLumaQP[50];
+  sDqLayer.pLumaQp = iLumaQP;
+
+  uint8_t iY[50] = {0};
+  sFilter.pCsData[0] = iY;
+  sFilter.iCsStride[0] = sFilter.iCsStride[1] = 4;
+
+  sDqLayer.iMbX = 0;
+  sDqLayer.iMbY = 0; //Only for test easy
+  sDqLayer.iMbXyIndex = 1;  // this function has NO iMbXyIndex validation
+
+#define UT_DB_LUMA_TEST(iFlag, iQP, iV0, iV1, iV2) \
+  iBoundryFlag = iFlag; \
+  memset(iLumaQP, iQP, sizeof(int8_t)*50); \
+  memset(iY, 0, sizeof(uint8_t)*50); \
+  FilteringEdgeLumaHV(&sDqLayer, &sFilter, iBoundryFlag); \
+  EXPECT_TRUE(iY[0]==iV0); \
+  EXPECT_TRUE(iY[1<<2]==iV1 && iY[2<<2]==iV1 && iY[3<<2]==iV1); \
+  EXPECT_TRUE(iY[(1 << 2)*sFilter.iCsStride[0]]==iV2 && iY[(2 << 2)*sFilter.iCsStride[0]]==iV2 && iY[(3 << 2)*sFilter.iCsStride[0]]==iV2);
+
+  // QP<=15, iAlpha == iBeta == 0, TOP & LEFT
+  iQP = rand() % 16;
+  UT_DB_LUMA_TEST (0x03, iQP, 0, 0, 0)
+
+  // QP>=16, iAlpha>0 && iBeta>0, TOP & LEFT
+  iQP = 16 + rand() % 35;
+  UT_DB_LUMA_TEST (0x03, iQP, 2, 1, 1)
+
+  // QP<=15, iAlpha == iBeta == 0, TOP | LEFT
+  iQP = rand() % 16;
+  UT_DB_LUMA_TEST (0x01, iQP, 0, 0, 0)
+  iQP = rand() % 16;
+  UT_DB_LUMA_TEST (0x02, iQP, 0, 0, 0)
+
+  // QP>=16, iAlpha>0 && iBeta>0, TOP | LEFT
+  iQP = 16 + rand() % 35;
+  UT_DB_LUMA_TEST (0x01, iQP, 1, 1, 1)
+  iQP = 16 + rand() % 35;
+  UT_DB_LUMA_TEST (0x02, iQP, 1, 1, 1)
+
+  // QP<=15, iAlpha == iBeta == 0, !TOP & !LEFT
+  iQP = rand() % 16;
+  UT_DB_LUMA_TEST (0x00, iQP, 0, 0, 0)
+
+  // QP>=16, iAlpha>0 && iBeta>0, !TOP & !LEFT
+  iQP = 16 + rand() % 35;
+  UT_DB_LUMA_TEST (0x00, iQP, 0, 1, 1)
+}
+
+/////////// Bs calculation functions
+TEST (DecoderDeblocking, DeblockingBsMarginalMBAvcbase) {
+  // uint32_t DeblockingBsMarginalMBAvcbase (PDqLayer pCurDqLayer, int32_t iEdge, int32_t iNeighMb, int32_t iMbXy)
+  /* Calculate the Bs equal to 2 or 1 */
+  SDqLayer sDqLayer;
+
+  // Only define 2 MBs here
+  int8_t iNoZeroCount[24 * 2]; // (*pNzc)[24]
+  int8_t iLayerRefIndex[2][16 * 2]; // (*pRefIndex[LIST_A])[MB_BLOCK4x4_NUM];
+  int16_t iLayerMv[2][16 * 2][2]; //(*pMv[LIST_A])[MB_BLOCK4x4_NUM][MV_A];
+
+  sDqLayer.pNzc = (int8_t (*)[24])iNoZeroCount;
+  sDqLayer.pRefIndex[0] = (int8_t (*)[16])&iLayerRefIndex[0];
+  sDqLayer.pRefIndex[1] = (int8_t (*)[16])&iLayerRefIndex[1];
+
+  sDqLayer.pMv[0] = (int16_t (*) [16][2])&iLayerMv[0];
+  sDqLayer.pMv[1] = (int16_t (*) [16][2])&iLayerMv[1];
+
+#define UT_DB_CLEAN_STATUS \
+  memset(iNoZeroCount, 0, sizeof(int8_t)*24*2); \
+  memset(iLayerRefIndex, 0, sizeof(int8_t)*2*16*2); \
+  memset(iLayerMv, 0, sizeof(int16_t)*2*16*2*2);
+
+  int32_t iCurrBlock, iNeighborBlock;
+
+  /* Cycle for each block and its neighboring block */
+  for (int iEdge = 0; iEdge < 2; iEdge++) { // Vertical and Horizontal
+    for (int iPos = 0; iPos < 4; iPos++) { // Four different blocks on the edge
+      iCurrBlock = (iEdge == 0 ? 4 * iPos : iPos);
+      iNeighborBlock = (iEdge == 0 ? (3 + iPos * 4) : (12 + iPos));
+
+      // (1) iEdge == 0, current block NoZeroCount != 0
+      UT_DB_CLEAN_STATUS
+      iNoZeroCount[0 * 24 + iCurrBlock] = 1; // Current MB_block position
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (2 << (iPos * 8))) << iEdge << " " << iPos << " NoZeroCount!=0";
+
+      // (2) iEdge == 0, neighbor block NoZeroCount != 0
+      UT_DB_CLEAN_STATUS
+      iNoZeroCount[1 * 24 + iNeighborBlock ] = 1; // Neighbor MB_block position
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (2 << (iPos * 8))) << iEdge << " " << iPos << " NoZeroCount!=0";
+
+      // (3) iEdge == 0, reference idx diff
+      UT_DB_CLEAN_STATUS
+      iLayerRefIndex[0][0 * 16 + iCurrBlock] = 0;
+      iLayerRefIndex[0][1 * 16 + iNeighborBlock] = 1;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (1 << (iPos * 8))) << iEdge << " " << iPos << " Ref idx diff";
+
+      // (4) iEdge == 0, abs(mv diff) < 4
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][0 * 16 + iCurrBlock][0] = rand() % 4;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1, 0) == 0) << iEdge << " " << iPos << " diff_mv < 4";
+
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][0 * 16 + iCurrBlock][1] = rand() % 4;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1, 0) == 0) << iEdge << " " << iPos << " diff_mv < 4";
+
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][1 * 16 + iNeighborBlock][0] = rand() % 4;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1, 0) == 0) << iEdge << " " << iPos << " diff_mv < 4";
+
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][1 * 16 + iNeighborBlock][1] = rand() % 4;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1, 0) == 0) << iEdge << " " << iPos << " diff_mv < 4";
+
+      // (5) iEdge == 0, abs(mv diff) > 4
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][0 * 16 + iCurrBlock][0] = 4;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (1 << (iPos * 8))) << iEdge << " " << iPos << " diff_mv == 4";
+
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][0 * 16 + iCurrBlock][1] = 4;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (1 << (iPos * 8))) << iEdge << " " << iPos << " diff_mv == 4";
+
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][1 * 16 + iNeighborBlock][0] = 4;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (1 << (iPos * 8))) << iEdge << " " << iPos << " diff_mv == 4";
+
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][1 * 16 + iNeighborBlock][1] = 4;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (1 << (iPos * 8))) << iEdge << " " << iPos << " diff_mv == 4";
+
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][0 * 16 + iCurrBlock][0] = -2048;
+      iLayerMv[0][1 * 16 + iNeighborBlock][0] = 2047;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (1 << (iPos * 8))) << iEdge << " " << iPos << " diff_mv == maximum";
+
+      UT_DB_CLEAN_STATUS
+      iLayerMv[0][0 * 16 + iCurrBlock][1] = -2048;
+      iLayerMv[0][1 * 16 + iNeighborBlock][1] = 2047;
+      EXPECT_TRUE (DeblockingBsMarginalMBAvcbase (&sDqLayer, iEdge, 1,
+                   0) == (1 << (iPos * 8))) << iEdge << " " << iPos << " diff_mv == maximum";
+    }
+  }
+}
+
+TEST (Deblocking, WelsDeblockingMb) {
+  // void WelsDeblockingMb (PDqLayer pCurDqLayer, PDeblockingFilter  pFilter, int32_t iBoundryFlag)
+  /* Deblock one MB, calculate the Bs inside the function, only consider the intra / intra block */
+  SDqLayer sDqLayer;
+
+  SDeblockingFilter sFilter;
+  SDeblockingFunc sDBFunc;
+  sFilter.pLoopf = &sDBFunc;
+  sFilter.pLoopf->pfChromaDeblockingLT4Hor = &UT_DeblockingFuncChromaLT4Func;
+  sFilter.pLoopf->pfChromaDeblockingLT4Ver = &UT_DeblockingFuncChromaLT4Func;
+  sFilter.pLoopf->pfChromaDeblockingEQ4Hor = &UT_DeblockingFuncChromaEQ4Func;
+  sFilter.pLoopf->pfChromaDeblockingEQ4Ver = &UT_DeblockingFuncChromaEQ4Func;
+  sFilter.pLoopf->pfLumaDeblockingLT4Hor = &UT_DeblockingFuncLumaLT4Func;
+  sFilter.pLoopf->pfLumaDeblockingEQ4Hor = &UT_DeblockingFuncLumaEQ4Func;
+  sFilter.pLoopf->pfLumaDeblockingLT4Ver = &UT_DeblockingFuncLumaLT4Func;
+  sFilter.pLoopf->pfLumaDeblockingEQ4Ver = &UT_DeblockingFuncLumaEQ4Func;
+
+  sDqLayer.iMbX = sDqLayer.iMbY = 0;
+  sDqLayer.iMbXyIndex = 1;
+  sDqLayer.iMbWidth = 1;
+
+  uint8_t iY[50] = {0};
+  sFilter.pCsData[0] = iY;
+  sFilter.iCsStride[0] = 4;
+
+  uint8_t iCb[9] = {0};
+  uint8_t iCr[9] = {0};
+  sFilter.pCsData[1] = iCb;
+  sFilter.pCsData[2] = iCr;
+  sFilter.iCsStride[1] = 2;
+
+  int8_t iLumaQP[50] = {0};
+  int8_t iChromaQP[9] = {0};
+  sDqLayer.pLumaQp = iLumaQP;
+  sDqLayer.pChromaQp = iChromaQP;
+
+  int8_t iMbType[2];
+  sDqLayer.pMbType = iMbType;
+  sDqLayer.pMbType[0] = 0x01;
+  sDqLayer.pMbType[1] = 0x01;
+
+  sFilter.iSliceAlphaC0Offset = 0;
+  sFilter.iSliceBetaOffset = 0;
+
+  int32_t iQP;
+
+#define UT_DB_MACROBLOCK_TEST( iBoundFlag, iQP, iLumaV0, iLumaV1, iLumaV2, iChromaV0, iChromaV1, iChromaV2 ) \
+  memset(sDqLayer.pLumaQp, iQP, sizeof(int8_t)*50); \
+  memset(sDqLayer.pChromaQp, iQP, sizeof(int8_t)*9); \
+  memset(sFilter.pCsData[0], 0, sizeof(int8_t)*50); \
+  memset(sFilter.pCsData[1], 0, sizeof(int8_t)*9); \
+  memset(sFilter.pCsData[2], 0, sizeof(int8_t)*9); \
+  WelsDeblockingMb(&sDqLayer, &sFilter, iBoundFlag ); \
+  EXPECT_TRUE(iY[0]==iLumaV0)<<iQP<<" "<<sDqLayer.pMbType[1]; \
+  EXPECT_TRUE(iY[1<<2]==iLumaV1 && iY[2<<2]==iLumaV1 && iY[3<<2]==iLumaV1)<<iQP<<" "<<sDqLayer.pMbType[1]; \
+  EXPECT_TRUE(iY[(1 << 2)*sFilter.iCsStride[0]]==iLumaV2 && iY[(2 << 2)*sFilter.iCsStride[0]]==iLumaV2 && iY[(3 << 2)*sFilter.iCsStride[0]]==iLumaV2)<<iQP<<" "<<sDqLayer.pMbType[1]; \
+  EXPECT_TRUE(iCb[0]==iChromaV0 && iCr[0]==iChromaV0)<<iQP<<" "<<sDqLayer.pMbType[1]; \
+  EXPECT_TRUE(iCb[2<<1]==iChromaV1 && iCr[2<<1]==iChromaV1)<<iQP<<" "<<sDqLayer.pMbType[1]; \
+  EXPECT_TRUE(iCb[(2<<1)*sFilter.iCsStride[1]]==iChromaV2 && iCr[(2<<1)*sFilter.iCsStride[1]]==iChromaV2)<<iQP<<" "<<sDqLayer.pMbType[1];
+
+  // QP>16, LEFT & TOP, Intra mode 0x01
+  iQP = 16 + rand() % 35;
+  sDqLayer.pMbType[1] = 0x01;
+  UT_DB_MACROBLOCK_TEST (0x03, iQP, 2, 1, 1, 2, 1, 1)
+
+  // QP>16, LEFT & TOP, Intra mode 0x02
+  iQP = 16 + rand() % 35;
+  sDqLayer.pMbType[1] = 0x02;
+  UT_DB_MACROBLOCK_TEST (0x03, iQP, 2, 1, 1, 2, 1, 1)
+
+  // MbType==0x03, Intra8x8 has not been supported now.
+
+  // QP>16, LEFT & TOP, Intra mode 0x04
+  iQP = 16 + rand() % 35;
+  sDqLayer.pMbType[1] = 0x04;
+  UT_DB_MACROBLOCK_TEST (0x03, iQP, 2, 1, 1, 2, 1, 1)
+
+  // QP>16, LEFT & TOP, neighbor is Intra
+  iQP = 16 + rand() % 35;
+  sDqLayer.pMbType[0] = 0x02;
+  sDqLayer.pMbType[1] = 0x0f; // Internal SKIP, Bs==0
+  UT_DB_MACROBLOCK_TEST (0x03, iQP, 2, 0, 0, 2, 0, 0)
+
+  // QP<15, no output
+  iQP = rand() % 16;
+  sDqLayer.pMbType[1] = 0x04;
+  UT_DB_MACROBLOCK_TEST (0x03, iQP, 0, 0, 0, 0, 0, 0)
 }
\ No newline at end of file