shithub: openh264

Download patch

ref: b4be8f3f1a6a42e3edf51ddf59b9fb8f2ac55572
parent: 54f5f3472565c2ad5a38b80d7c3154622507723a
parent: bbe016543fd2d82f941318901af1d40231f36869
author: Licai Guo <licaguo@cisco.com>
date: Wed Mar 26 12:37:49 EDT 2014

Merge pull request #586 from sijchen/me_merge72

[Encoder ME] Add basic cross search functions and its unit tests

--- a/codec/encoder/core/inc/svc_motion_estimate.h
+++ b/codec/encoder/core/inc/svc_motion_estimate.h
@@ -67,7 +67,7 @@
 uint32_t					uiSadCostThreshold;
 int32_t						iCurMeBlockPixX;
 int32_t						iCurMeBlockPixY;
-uint8_t						uiPixel;   /* PIXEL_WxH */
+uint8_t						uiBlockSize;   /* BLOCK_WxH */
 uint8_t						uiReserved;
 
 uint8_t*						pEncMb;
@@ -147,7 +147,14 @@
                       const SMVUnitXY ksMinMv, const SMVUnitXY ksMaxMv, const int32_t kiEncStride, const int32_t kiRefStride,
                       int32_t& iBestSadCost);
 
-inline void SetMvWithinMvRange( const int32_t kiMbWidth, const int32_t kiMbHeight, const int32_t kiMbX, const int32_t kiMbY,
+void LineFullSearch_c(	PSampleSadSatdCostFunc pSad, void *vpMe,
+                        uint16_t* pMvdTable, const int32_t kiFixedMvd,
+                        const int32_t kiEncStride, const int32_t kiRefStride,
+                        const int32_t kiMinPos, const int32_t kiMaxPos,
+                        const bool bVerticalSearch );
+void WelsMotionCrossSearch(SWelsFuncPtrList *pFuncList,  SDqLayer* pCurLayer, SWelsME * pMe, const SSlice* pSlice);
+
+inline void SetMvWithinIntegerMvRange( const int32_t kiMbWidth, const int32_t kiMbHeight, const int32_t kiMbX, const int32_t kiMbY,
                         const int32_t kiMaxMvRange,
                         SMVUnitXY* pMvMin, SMVUnitXY* pMvMax)
 {
--- a/codec/encoder/core/src/encoder_ext.cpp
+++ b/codec/encoder/core/src/encoder_ext.cpp
@@ -2499,7 +2499,7 @@
 
 void PreprocessSliceCoding (sWelsEncCtx* pCtx) {
   SDqLayer* pCurLayer		= pCtx->pCurDqLayer;
-  const bool kbBaseAvail	= pCurLayer->bBaseLayerAvailableFlag;
+  //const bool kbBaseAvail	= pCurLayer->bBaseLayerAvailableFlag;
   const bool kbHighestSpatialLayer	=
     (pCtx->pSvcParam->iSpatialLayerNum == (pCurLayer->sLayerInfo.sNalHeaderExt.uiDependencyId + 1));
   SWelsFuncPtrList* pFuncList = pCtx->pFuncList;
--- a/codec/encoder/core/src/md.cpp
+++ b/codec/encoder/core/src/md.cpp
@@ -536,7 +536,7 @@
   int32_t iCurCost;
   uint8_t* pEncMb				= pMe->pEncMb;
   uint8_t* pTmp				= NULL;
-  const uint8_t kuiPixel		= pMe->uiPixel;
+  const uint8_t kuiPixel		= pMe->uiBlockSize;
 
   pSampleAvg[kiAvgIndex] (pMeRefine->pQuarPixTmp, ME_REFINE_BUF_STRIDE, pParams->pSrcA[0], ME_REFINE_BUF_STRIDE,
                           pParams->pSrcB[0], pParams->iStrideA, kiHeight);
@@ -603,7 +603,7 @@
       && (pFunc->sSampleDealingFuncs.pfMdCost == pFunc->sSampleDealingFuncs.pfSampleSatd)) {
     iBestCost = pMe->uSadPredISatd.uiSatd + COST_MVD (pMe->pMvdCost, iMvx - pMe->sMvp.iMvX, iMvy - pMe->sMvp.iMvY);
   } else {
-    iBestCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc, pRef, kiStrideRef) +
+    iBestCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc, pRef, kiStrideRef) +
                 COST_MVD (pMe->pMvdCost, iMvx - pMe->sMvp.iMvX, iMvy - pMe->sMvp.iMvY);
   }
 
@@ -614,7 +614,7 @@
 
   //step 1: get [iWidth][iHeight+1] half pixel from vertical filter
   //===========================(0, -2)==============================//
-  iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc, pMeRefine->pHalfPixV,
+  iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc, pMeRefine->pHalfPixV,
              ME_REFINE_BUF_STRIDE) +
              COST_MVD (pMe->pMvdCost, iMvx - pMe->sMvp.iMvX, iMvy - 2 - pMe->sMvp.iMvY);
   if (iCurCost < iBestCost) {
@@ -623,7 +623,7 @@
     pBestPredInter = pMeRefine->pHalfPixV;
   }
   //===========================(0, 2)==============================//
-  iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc,
+  iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc,
              pMeRefine->pHalfPixV + ME_REFINE_BUF_STRIDE, ME_REFINE_BUF_STRIDE) +
              COST_MVD (pMe->pMvdCost, iMvx - pMe->sMvp.iMvX, iMvy + 2 - pMe->sMvp.iMvY);
   if (iCurCost < iBestCost) {
@@ -636,7 +636,7 @@
   //step 2: get [iWidth][iHeight+1] half pixel from horizon filter
 
   //===========================(-2, 0)==============================//
-  iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc, pMeRefine->pHalfPixH,
+  iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc, pMeRefine->pHalfPixH,
              ME_REFINE_BUF_STRIDE) +
              COST_MVD (pMe->pMvdCost, iMvx - 2 - pMe->sMvp.iMvX, iMvy - pMe->sMvp.iMvY);
   if (iCurCost < iBestCost) {
@@ -645,7 +645,7 @@
     pBestPredInter = pMeRefine->pHalfPixH;
   }
   //===========================(2, 0)===============================//
-  iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiPixel] (pEncData, kiStrideEnc, pMeRefine->pHalfPixH + 1,
+  iCurCost = pFunc->sSampleDealingFuncs.pfMeCost[pMe->uiBlockSize] (pEncData, kiStrideEnc, pMeRefine->pHalfPixH + 1,
              ME_REFINE_BUF_STRIDE) +
              COST_MVD (pMe->pMvdCost, iMvx + 2 - pMe->sMvp.iMvX, iMvy - pMe->sMvp.iMvY);
   if (iCurCost < iBestCost) {
--- a/codec/encoder/core/src/svc_base_layer_md.cpp
+++ b/codec/encoder/core/src/svc_base_layer_md.cpp
@@ -359,7 +359,7 @@
   ST32 (&pCurMb->sP16x16Mv, 0);
   ST32 (&pCurLayer->pDecPic->sMvList[kiMbXY], 0);
 
-  SetMvWithinMvRange( kiMbWidth, kiMbHeight, kiMbX, kiMbY, CAMERA_STARTMV_RANGE, &(pSlice->sMvStartMin), &(pSlice->sMvStartMax));
+  SetMvWithinIntegerMvRange( kiMbWidth, kiMbHeight, kiMbX, kiMbY, CAMERA_STARTMV_RANGE, &(pSlice->sMvStartMin), &(pSlice->sMvStartMax));
 }
 
 int32_t WelsMdI16x16 (SWelsFuncPtrList* pFunc, SDqLayer* pCurDqLayer, SMbCache* pMbCache, int32_t iLambda) {
@@ -975,7 +975,7 @@
 {
   sWelsMe.iCurMeBlockPixX = sWelsMd.iMbPixX;
   sWelsMe.iCurMeBlockPixY = sWelsMd.iMbPixY;
-  sWelsMe.uiPixel = iBlockSize;
+  sWelsMe.uiBlockSize = iBlockSize;
   sWelsMe.pMvdCost = sWelsMd.pMvdCost;
 
   sWelsMe.pEncMb = pEnc;
--- a/codec/encoder/core/src/svc_motion_estimate.cpp
+++ b/codec/encoder/core/src/svc_motion_estimate.cpp
@@ -81,7 +81,7 @@
     MeEndIntepelSearch(pMe);
   }
 
-  pFuncList->pfCalculateSatd( pFuncList->sSampleDealingFuncs.pfSampleSatd[pMe->uiPixel], pMe, iStrideEnc, iStrideRef );
+  pFuncList->pfCalculateSatd( pFuncList->sSampleDealingFuncs.pfSampleSatd[pMe->uiBlockSize], pMe, iStrideEnc, iStrideRef );
 }
 
 /*!
@@ -96,7 +96,7 @@
  */
 bool WelsMotionEstimateInitialPoint (SWelsFuncPtrList* pFuncList, SWelsME* pMe, SSlice* pSlice, int32_t iStrideEnc,
                                      int32_t iStrideRef) {
-  PSampleSadSatdCostFunc pSad		= pFuncList->sSampleDealingFuncs.pfSampleSad[pMe->uiPixel];
+  PSampleSadSatdCostFunc pSad		= pFuncList->sSampleDealingFuncs.pfSampleSad[pMe->uiBlockSize];
   const uint16_t* kpMvdCost	= pMe->pMvdCost;
   uint8_t* const kpEncMb		= pMe->pEncMb;
   int16_t iMvc0, iMvc1;
@@ -197,7 +197,7 @@
 
 void WelsMotionEstimateIterativeSearch (SWelsFuncPtrList* pFuncList, SWelsME* pMe, const int32_t kiStrideEnc,
                                         const int32_t kiStrideRef, uint8_t* pFref) {
-  PSample4SadCostFunc			pSad					=  pFuncList->sSampleDealingFuncs.pfSample4Sad[pMe->uiPixel];
+  PSample4SadCostFunc			pSad					=  pFuncList->sSampleDealingFuncs.pfSample4Sad[pMe->uiBlockSize];
 
   uint8_t* const kpEncMb = pMe->pEncMb;
   const uint16_t* kpMvdCost = pMe->pMvdCost;
@@ -255,7 +255,7 @@
   const int16_t kiMvY = pMe->sDirectionalMv.iMvY;
 
   //Check MV from scrolling detection
-  if ( (BLOCK_16x16!=pMe->uiPixel) //scrolled_MV with P16x16 is checked SKIP checking function
+  if ( (BLOCK_16x16!=pMe->uiBlockSize) //scrolled_MV with P16x16 is checked SKIP checking function
     && ( kiMvX | kiMvY ) //(0,0) checked in ordinary initial point checking
     && CheckMvInRange( pMe->sDirectionalMv, ksMinMv, ksMaxMv ) )
   {
@@ -278,4 +278,70 @@
   return false;
 }
 
+void LineFullSearch_c(	PSampleSadSatdCostFunc pSad, void *vpMe,
+													uint16_t* pMvdTable, const int32_t kiFixedMvd,
+													const int32_t kiEncStride, const int32_t kiRefStride,
+													const int32_t kiMinPos, const int32_t kiMaxPos,
+                          const bool bVerticalSearch )
+{
+  SWelsME *pMe				                    = static_cast<SWelsME *>(vpMe);
+  const int32_t kiCurMeBlockPix	= bVerticalSearch?pMe->iCurMeBlockPixY:pMe->iCurMeBlockPixX;
+  const int32_t kiStride = bVerticalSearch?kiRefStride:1;
+  uint8_t* pRef			      = &pMe->pColoRefMb[(kiMinPos - kiCurMeBlockPix)*kiStride];
+  uint16_t* pMvdCost  = &(pMvdTable[kiMinPos<<2]);
+  uint32_t uiBestCost	  = 0xFFFFFFFF;
+  int32_t iBestPos		   = 0;
+
+  for ( int32_t iTargetPos = kiMinPos; iTargetPos < kiMaxPos; ++ iTargetPos ) {
+    uint8_t* const kpEncMb	= pMe->pEncMb;
+    uint32_t uiSadCost = pSad( kpEncMb, kiEncStride, pRef, kiRefStride ) + (kiFixedMvd + *pMvdCost);
+    if (uiSadCost < uiBestCost) {
+      uiBestCost	= uiSadCost;
+      iBestPos	= iTargetPos;
+    }
+    pRef += kiStride;
+    pMvdCost+=4;
+  }
+
+  if (uiBestCost < pMe->uiSadCost) {
+    SMVUnitXY sBestMv;
+    sBestMv.iMvX = bVerticalSearch?0:(iBestPos - kiCurMeBlockPix);
+    sBestMv.iMvY = bVerticalSearch?(iBestPos - kiCurMeBlockPix):0;
+    UpdateMeResults( sBestMv, uiBestCost, &pMe->pColoRefMb[sBestMv.iMvY*kiStride], pMe );
+  }
+}
+
+void WelsMotionCrossSearch(SWelsFuncPtrList *pFuncList,  SDqLayer* pCurLayer, SWelsME * pMe,
+											const SSlice* pSlice)
+{
+  //TODO:
+  //PMOTION_VERFULL_SEARCH VerticalFullSearchFunc	= pFuncList->VerticalFullSearch_c;
+  //PMOTION_HORFULL_SEARCH HorizontalFullSearchFunc	= pFuncList->HorizontalFullSearch_c;
+  const int32_t kiEncStride = pCurLayer->iEncStride[0];
+  const int32_t kiRefStride = pCurLayer->pRefPic->iLineSize[0];
+
+  const int32_t iCurMeBlockPixX = pMe->iCurMeBlockPixX;
+  const int32_t iCurMeBlockQpelPixX = ((iCurMeBlockPixX)<<2);
+  const int32_t iCurMeBlockPixY = pMe->iCurMeBlockPixY;
+  const int32_t iCurMeBlockQpelPixY = ((iCurMeBlockPixY)<<2);
+  uint16_t* pMvdCostX = pMe->pMvdCost - iCurMeBlockQpelPixX - pMe->sMvp.iMvX;//do the offset here instead of in the search
+  uint16_t* pMvdCostY = pMe->pMvdCost - iCurMeBlockQpelPixY - pMe->sMvp.iMvY;//do the offset here instead of in the search
+
+  //vertical search
+  LineFullSearch_c( pFuncList->sSampleDealingFuncs.pfSampleSad[pMe->uiBlockSize], pMe,
+    pMvdCostY, pMvdCostX[ iCurMeBlockQpelPixX ],
+    kiEncStride, kiRefStride,
+    iCurMeBlockPixY + pSlice->sMvStartMin.iMvY,
+    iCurMeBlockPixY + pSlice->sMvStartMax.iMvY, true );
+
+  //horizontal search
+  if (pMe->uiSadCost >= pMe->uiSadCostThreshold) {
+    LineFullSearch_c( pFuncList->sSampleDealingFuncs.pfSampleSad[pMe->uiBlockSize], pMe,
+      pMvdCostX, pMvdCostY[ iCurMeBlockQpelPixY ],
+      kiEncStride, kiRefStride,
+      iCurMeBlockPixX + pSlice->sMvStartMin.iMvX,
+      iCurMeBlockPixX + pSlice->sMvStartMax.iMvX,
+      false );
+  }
+}
 } // namespace WelsSVCEnc
--- a/test/encoder/EncUT_MotionEstimate.cpp
+++ b/test/encoder/EncUT_MotionEstimate.cpp
@@ -104,13 +104,13 @@
       CopyTargetBlock( m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
 
       //clean the sMe status
-      sMe.uiPixel = rand()%5;
+      sMe.uiBlockSize = rand()%5;
       sMe.pEncMb = m_pSrcBlock;
       sMe.pRefMb = pRefPicCenter;
       sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
       sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
       WelsMotionEstimateIterativeSearch (&sFuncList, &sMe, m_iMaxSearchBlock,
-        m_iWidth, pRefPicCenter);
+                                         m_iWidth, pRefPicCenter);
 
       //the last selection may be affected by MVDcost, that is when (0,0) will be better
       //when comparing (1,1) and (1,0), due to the difference between MVD cost, it is possible that (1,0) is selected while the best match is (1,1)
@@ -123,3 +123,129 @@
     }
   }
 }
+
+
+TEST_F(MotionEstimateTest, TestVerticalSearch)
+{
+  const int32_t kiMaxBlock16Sad = 72000;//a rough number
+  SWelsFuncPtrList sFuncList;
+  SWelsME sMe;
+
+  srand((uint32_t)time(NULL));
+  const uint8_t kuiQp = rand()%52;
+  InitMe(kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
+
+  SMVUnitXY sTargetMv;
+  WelsInitSampleSadFunc( &sFuncList, 0 );//test c functions
+
+  uint8_t *pRefPicCenter = m_pRefPic+(m_iHeight/2)*m_iWidth+(m_iWidth/2);
+  sMe.iCurMeBlockPixX = (m_iWidth/2);
+  sMe.iCurMeBlockPixY = (m_iHeight/2);
+
+  bool bDataGeneratorSucceed = false;
+  bool bFoundMatch = false;
+  int32_t iTryTimes=100;
+
+  sTargetMv.iMvX = 0;
+  sTargetMv.iMvY = WELS_MAX(INTPEL_NEEDED_MARGIN, rand()%m_iHeight-INTPEL_NEEDED_MARGIN);
+  bDataGeneratorSucceed = false;
+  bFoundMatch = false;
+  while (!bFoundMatch && (iTryTimes--)>0) {
+    if (!YUVPixelDataGenerator( m_pRefPic, m_iWidth, m_iHeight, m_iWidth ))
+      continue;
+
+    bDataGeneratorSucceed = true;
+    CopyTargetBlock( m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
+
+    //clean the sMe status
+    sMe.uiBlockSize = rand()%5;
+    sMe.pEncMb = m_pSrcBlock;
+    sMe.pRefMb = pRefPicCenter;
+    sMe.pColoRefMb = pRefPicCenter;
+    sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
+    sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
+    const int32_t iCurMeBlockPixX = sMe.iCurMeBlockPixX;
+    const int32_t iCurMeBlockQpelPixX = ((iCurMeBlockPixX)<<2);
+    const int32_t iCurMeBlockPixY = sMe.iCurMeBlockPixY;
+    const int32_t iCurMeBlockQpelPixY = ((iCurMeBlockPixY)<<2);
+    uint16_t* pMvdCostX = sMe.pMvdCost - iCurMeBlockQpelPixX - sMe.sMvp.iMvX;	//do the offset here
+    uint16_t* pMvdCostY = sMe.pMvdCost - iCurMeBlockQpelPixY - sMe.sMvp.iMvY;
+    LineFullSearch_c ( sFuncList.sSampleDealingFuncs.pfSampleSad[sMe.uiBlockSize], &sMe,
+                      pMvdCostY, pMvdCostX[ iCurMeBlockQpelPixX ],
+                      m_iMaxSearchBlock, m_iWidth,
+                      INTPEL_NEEDED_MARGIN,
+                      m_iHeight-INTPEL_NEEDED_MARGIN, true );
+
+    //the last selection may be affected by MVDcost, that is when smaller MvY will be better
+    bFoundMatch = (sMe.sMv.iMvX==0
+                   &&(sMe.sMv.iMvY==sTargetMv.iMvY||abs(sMe.sMv.iMvY)<abs(sTargetMv.iMvY)));
+    //printf("TestVerticalSearch Target: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY);
+  }
+  if (bDataGeneratorSucceed) {
+    //if DataGenerator never succeed, there is no meaning to check iTryTimes
+    ASSERT_TRUE(iTryTimes > 0);
+    //it is possible that ref at differnt position is identical, but that should be under a low probability
+  }
+}
+TEST_F(MotionEstimateTest, TestHorizontalSearch)
+{
+  const int32_t kiMaxBlock16Sad = 72000;//a rough number
+  SWelsFuncPtrList sFuncList;
+  SWelsME sMe;
+
+  srand((uint32_t)time(NULL));
+  const uint8_t kuiQp = rand()%52;
+  InitMe(kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
+
+  SMVUnitXY sTargetMv;
+  WelsInitSampleSadFunc( &sFuncList, 0 );//test c functions
+
+  uint8_t *pRefPicCenter = m_pRefPic+(m_iHeight/2)*m_iWidth+(m_iWidth/2);
+  sMe.iCurMeBlockPixX = (m_iWidth/2);
+  sMe.iCurMeBlockPixY = (m_iHeight/2);
+
+  bool bDataGeneratorSucceed = false;
+  bool bFoundMatch = false;
+  int32_t iTryTimes=100;
+
+  sTargetMv.iMvX = WELS_MAX(INTPEL_NEEDED_MARGIN, rand()%m_iWidth-INTPEL_NEEDED_MARGIN);
+  sTargetMv.iMvY = 0;
+  bDataGeneratorSucceed = false;
+  bFoundMatch = false;
+  while (!bFoundMatch && (iTryTimes--)>0) {
+    if (!YUVPixelDataGenerator( m_pRefPic, m_iWidth, m_iHeight, m_iWidth ))
+      continue;
+
+    bDataGeneratorSucceed = true;
+    CopyTargetBlock( m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
+
+    //clean the sMe status
+    sMe.uiBlockSize = rand()%5;
+    sMe.pEncMb = m_pSrcBlock;
+    sMe.pRefMb = pRefPicCenter;
+    sMe.pColoRefMb = pRefPicCenter;
+    sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
+    sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
+    const int32_t iCurMeBlockPixX = sMe.iCurMeBlockPixX;
+    const int32_t iCurMeBlockQpelPixX = ((iCurMeBlockPixX)<<2);
+    const int32_t iCurMeBlockPixY = sMe.iCurMeBlockPixY;
+    const int32_t iCurMeBlockQpelPixY = ((iCurMeBlockPixY)<<2);
+    uint16_t* pMvdCostX = sMe.pMvdCost - iCurMeBlockQpelPixX - sMe.sMvp.iMvX;	//do the offset here
+    uint16_t* pMvdCostY = sMe.pMvdCost - iCurMeBlockQpelPixY - sMe.sMvp.iMvY;
+    LineFullSearch_c ( sFuncList.sSampleDealingFuncs.pfSampleSad[sMe.uiBlockSize], &sMe,
+                      pMvdCostX, pMvdCostY[ iCurMeBlockQpelPixY ],
+                      m_iMaxSearchBlock, m_iWidth,
+                      INTPEL_NEEDED_MARGIN,
+                      m_iWidth-INTPEL_NEEDED_MARGIN, false );
+
+    //the last selection may be affected by MVDcost, that is when smaller MvY will be better
+    bFoundMatch = (sMe.sMv.iMvY==0
+                   &&(sMe.sMv.iMvX==sTargetMv.iMvX||abs(sMe.sMv.iMvX)<abs(sTargetMv.iMvX)));
+    //printf("TestHorizontalSearch Target: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY);
+  }
+  if (bDataGeneratorSucceed) {
+    //if DataGenerator never succeed, there is no meaning to check iTryTimes
+    ASSERT_TRUE(iTryTimes > 0);
+    //it is possible that ref at differnt position is identical, but that should be under a low probability
+  }
+}
\ No newline at end of file