• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdlib.h>
2 #include "gtest/gtest.h"
3 #include "utils/DataGenerator.h"
4 #include "md.h"
5 #include "sample.h"
6 #include "svc_motion_estimate.h"
7 #include "wels_func_ptr_def.h"
8 #include "cpu.h"
9 
10 using namespace WelsEnc;
11 
CopyTargetBlock(uint8_t * pSrcBlock,const int32_t kiBlockSize,SMVUnitXY sTargetMv,const int32_t kiRefPicStride,uint8_t * pRefPic)12 void CopyTargetBlock (uint8_t* pSrcBlock, const int32_t kiBlockSize, SMVUnitXY sTargetMv, const int32_t kiRefPicStride,
13                       uint8_t* pRefPic) {
14   uint8_t* pTargetPos = pRefPic + sTargetMv.iMvY * kiRefPicStride + sTargetMv.iMvX;
15   uint8_t* pSourcePos = pSrcBlock;
16 
17   for (int i = 0; i < kiBlockSize; i++) {
18     memcpy (pSourcePos, pTargetPos, kiBlockSize * sizeof (uint8_t));
19     pTargetPos += kiRefPicStride;
20     pSourcePos += kiBlockSize;
21   }
22 }
23 
24 
InitMe(const uint8_t kuiQp,const uint32_t kuiMvdTableMiddle,const uint32_t kuiMvdTableStride,uint16_t * pMvdCostTable,SWelsME * pMe)25 void InitMe (const uint8_t kuiQp, const uint32_t kuiMvdTableMiddle, const uint32_t kuiMvdTableStride,
26              uint16_t* pMvdCostTable, SWelsME* pMe) {
27   MvdCostInit (pMvdCostTable, kuiMvdTableStride);
28   pMe->pMvdCost = &pMvdCostTable[kuiQp * kuiMvdTableStride + kuiMvdTableMiddle];
29   pMe->sMvp.iMvX = pMe->sMvp.iMvY = 0;
30   pMe->sMvBase.iMvX = pMe->sMvBase.iMvY = 0;
31   pMe->sMv.iMvX = pMe->sMv.iMvY = 0;
32 }
33 
34 class MotionEstimateTest : public ::testing::Test {
35  public:
SetUp()36   virtual void SetUp() {
37     m_pRefData = NULL;
38     m_pSrcBlock = NULL;
39     m_pMvdCostTable = NULL;
40 
41     m_iWidth = 64;//size of search window
42     m_iHeight = 64;//size of search window
43     m_iMaxSearchBlock = 16;
44     m_uiMvdTableSize = (1 + (648 << 1));
45 
46     pMa = new CMemoryAlign (0);
47     m_pRefData = static_cast<uint8_t*>
48                  (pMa->WelsMalloc (m_iWidth * m_iHeight, "RefPic"));
49     ASSERT_TRUE (NULL != m_pRefData);
50     m_pSrcBlock = static_cast<uint8_t*>
51                   (pMa->WelsMalloc (m_iMaxSearchBlock * m_iMaxSearchBlock, "SrcBlock"));
52     ASSERT_TRUE (NULL != m_pSrcBlock);
53     m_pMvdCostTable = new uint16_t[52 * m_uiMvdTableSize];
54     ASSERT_TRUE (NULL != m_pMvdCostTable);
55   }
56   void DoLineTest (PLineFullSearchFunc func, bool horizontal);
TearDown()57   virtual void TearDown() {
58     delete [] m_pMvdCostTable;
59     pMa->WelsFree (m_pRefData, "RefPic");
60     pMa->WelsFree (m_pSrcBlock, "SrcBlock");
61     delete pMa;
62   }
63  public:
64   uint8_t* m_pRefData;
65   uint8_t* m_pSrcBlock;
66   uint32_t m_uiMvdTableSize;
67   uint16_t* m_pMvdCostTable;
68 
69   int32_t m_iWidth;
70   int32_t m_iHeight;
71   int32_t m_iMaxSearchBlock;
72   CMemoryAlign* pMa;
73 };
74 
75 
TEST_F(MotionEstimateTest,TestDiamondSearch)76 TEST_F (MotionEstimateTest, TestDiamondSearch) {
77 #define TEST_POS (5)
78   const int32_t kiPositionToCheck[TEST_POS][2] = {{0, 0}, {0, 1}, {1, 0}, {0, -1}, { -1, 0}};
79   const int32_t kiMaxBlock16Sad = 72000;//a rough number
80   SWelsFuncPtrList sFuncList;
81   SWelsME sMe;
82   SSlice sSlice;
83   memset (&sSlice, 0, sizeof (sSlice));
84 
85   const uint8_t kuiQp = rand() % 52;
86   InitMe (kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
87 
88   SMVUnitXY sTargetMv;
89   WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
90 
91   uint8_t* pRefPicCenter = m_pRefData + (m_iHeight / 2) * m_iWidth + (m_iWidth / 2);
92   bool bDataGeneratorSucceed = false;
93   bool bFoundMatch = false;
94   int32_t i, iTryTimes;
95   for (i = 0; i < TEST_POS; i++) {
96     sTargetMv.iMvX = kiPositionToCheck[i][0];
97     sTargetMv.iMvY = kiPositionToCheck[i][1];
98     iTryTimes = 100;
99     bDataGeneratorSucceed = false;
100     bFoundMatch = false;
101     while (!bFoundMatch && (iTryTimes--) > 0) {
102       if (!YUVPixelDataGenerator (m_pRefData, m_iWidth, m_iHeight, m_iWidth))
103         continue;
104 
105       bDataGeneratorSucceed = true;
106       CopyTargetBlock (m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
107 
108       //clean the sMe status
109       sMe.uiBlockSize = rand() % 5;
110       sMe.pEncMb = m_pSrcBlock;
111       sMe.pRefMb = pRefPicCenter;
112       sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
113       sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
114       WelsDiamondSearch (&sFuncList, &sMe, &sSlice, m_iMaxSearchBlock, m_iWidth);
115 
116       //the last selection may be affected by MVDcost, that is when (0,0) will be better
117       //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)
118       bFoundMatch = ((sMe.sMv.iMvX == (sTargetMv.iMvX)) || (sMe.sMv.iMvX == 0)) && ((sMe.sMv.iMvY == (sTargetMv.iMvY))
119                     || (sMe.sMv.iMvY == 0));
120     }
121     if (bDataGeneratorSucceed) {
122       //if DataGenerator never succeed, there is no meaning to check iTryTimes
123       ASSERT_TRUE (iTryTimes > 0);
124       //it is possible that ref at differnt position is identical, but that should be under a low probability
125     }
126   }
127 }
128 
129 class MotionEstimateRangeTest : public ::testing::Test {
130  public:
SetUp()131   virtual void SetUp() {
132 
133     m_iWidth = 320;
134     m_iHeight = 240;
135     m_iWidthExt = m_iWidth + 2 * PADDING_LENGTH;
136     m_iHeightExt = m_iHeight + 2 * PADDING_LENGTH;
137     m_iMbWidth = m_iWidth >> 4;
138     m_iMbHeight = m_iHeight >> 4;
139 
140     m_iUsageType = 0;
141     m_iNumDependencyLayers = 1;
142     m_iMvRange = m_iUsageType ? EXPANDED_MV_RANGE : CAMERA_STARTMV_RANGE;
143     m_iMvdRange = (m_iUsageType ? EXPANDED_MVD_RANGE : ((m_iNumDependencyLayers == 1) ? CAMERA_MVD_RANGE :
144                    CAMERA_HIGHLAYER_MVD_RANGE));
145     m_uiMvdInterTableSize       = (m_iMvdRange << 2); //intepel*4=qpel
146     m_uiMvdInterTableStride     =  1 + (m_uiMvdInterTableSize << 1);//qpel_mv_range*2=(+/-);
147     m_uiMvdCacheAlignedSize     = m_uiMvdInterTableStride * sizeof (uint16_t);
148 
149     m_pMa = new CMemoryAlign (16);
150     ASSERT_TRUE (NULL != m_pMa);
151     m_pMvdCostTable = (uint16_t*)m_pMa->WelsMallocz (52 * m_uiMvdCacheAlignedSize, "pMvdCostTable");
152     ASSERT_TRUE (NULL != m_pMvdCostTable);
153     m_pRefStart = (uint8_t*)m_pMa->WelsMallocz (m_iWidthExt * m_iHeightExt, "reference frame ");
154     ASSERT_TRUE (NULL != m_pRefStart);
155     m_pSrc = (uint8_t*)m_pMa->WelsMallocz (m_iWidth * m_iHeight, "source frame");
156     ASSERT_TRUE (NULL != m_pSrc);
157 
158   }
TearDown()159   virtual void TearDown() {
160     if (!m_pMa)
161       return;
162     if (m_pRefStart) {
163       m_pMa->WelsFree (m_pRefStart, "reference frame ");
164       m_pRefStart = NULL;
165     }
166     if (m_pSrc) {
167       m_pMa->WelsFree (m_pSrc, "source frame ");
168       m_pSrc = NULL;
169     }
170     if (m_pMvdCostTable) {
171       m_pMa->WelsFree (m_pMvdCostTable, "pMvdCostTable");
172       m_pMvdCostTable = NULL;
173     }
174     if (m_pMa) {
175       delete m_pMa;
176       m_pMa = NULL;
177     }
178   }
179  public:
180   uint8_t* m_pRefStart;
181   uint8_t* m_pSrc;
182   uint16_t* m_pMvdCostTable;
183 
184   int32_t m_iWidth;
185   int32_t m_iHeight;
186   int32_t m_iWidthExt;
187   int32_t m_iHeightExt;
188   int32_t m_iMbWidth;
189   int32_t m_iMbHeight;
190   CMemoryAlign* m_pMa;
191 
192   int32_t m_iMvRange;
193   int32_t m_iMvdRange;
194   uint32_t m_uiMvdInterTableSize;
195   uint32_t m_uiMvdInterTableStride;
196   uint32_t m_uiMvdCacheAlignedSize;
197   int32_t m_iUsageType;
198   int32_t m_iNumDependencyLayers;
199 };
200 
TEST_F(MotionEstimateRangeTest,TestDiamondSearch)201 TEST_F (MotionEstimateRangeTest, TestDiamondSearch) {
202 
203   const int32_t kiMaxBlock16Sad = 72000;//a rough number
204   uint8_t* pRef = m_pRefStart + PADDING_LENGTH * m_iWidthExt + PADDING_LENGTH;
205   SWelsFuncPtrList sFuncList;
206   SWelsME sMe;
207   SSlice sSlice;
208   const uint8_t kuiQp = rand() % 52;
209   InitMe (kuiQp, m_uiMvdInterTableSize, m_uiMvdInterTableStride, m_pMvdCostTable, &sMe);
210 
211   WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
212 
213   memset (&sSlice, 0, sizeof (sSlice));
214   memset (m_pSrc, 128, m_iWidth * m_iHeight);
215   memset (m_pRefStart, 0, m_iWidthExt * m_iHeightExt);
216 
217   sMe.uiBlockSize = BLOCK_16x16; //
218 
219   sMe.sMvp.iMvX = rand() % m_iMvRange;
220   sMe.sMvp.iMvY = rand() % m_iMvRange;
221 
222   for (int h = 0; h < m_iHeight; h++)
223     memset (pRef + h * m_iWidthExt, h, m_iWidthExt);
224 
225   sMe.pEncMb = m_pSrc;
226 
227   sMe.sMv.iMvX = sMe.sMvp.iMvX;
228   sMe.sMv.iMvY = sMe.sMvp.iMvY;
229 
230   sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
231   SetMvWithinIntegerMvRange (m_iMbWidth, m_iMbHeight,     0, 0, m_iMvRange,
232                              & (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
233 
234 
235   sMe.pRefMb = pRef + sMe.sMvp.iMvY * m_iWidthExt;
236   WelsDiamondSearch (&sFuncList, &sMe, &sSlice, m_iWidth, m_iWidthExt);
237 
238   if ((WELS_ABS (sMe.sMv.iMvX) > m_iMvRange))
239     printf ("mvx = %d\n", sMe.sMv.iMvX);
240   ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvX) > m_iMvRange));
241   if ((WELS_ABS (sMe.sMv.iMvY) > m_iMvRange))
242     printf ("mvy = %d\n", sMe.sMv.iMvY);
243   ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvY) > m_iMvRange));
244 
245 
246 }
247 
TEST_F(MotionEstimateRangeTest,TestWelsMotionCrossSearch)248 TEST_F (MotionEstimateRangeTest, TestWelsMotionCrossSearch) {
249 
250   SWelsFuncPtrList sFuncList;
251   SWelsME sMe;
252   SSlice sSlice;
253   bool bUsageType = true;
254   uint8_t* pRef = m_pRefStart + PADDING_LENGTH * m_iWidthExt + PADDING_LENGTH;
255   const int32_t kiMaxBlock16Sad = 72000;//a rough number
256 
257   memset (&sSlice, 0, sizeof (sSlice));
258   memset (&sMe, 0, sizeof (sMe));
259   WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
260   WelsInitMeFunc (&sFuncList, 0, bUsageType);
261 
262   RandomPixelDataGenerator (m_pSrc, m_iWidth, m_iHeight, m_iWidth);
263   RandomPixelDataGenerator (m_pRefStart, m_iWidthExt, m_iHeightExt, m_iWidthExt);
264 
265   sMe.uiBlockSize = BLOCK_16x16; //
266   for (int32_t iMby = 0; iMby < m_iMbHeight; iMby++) {
267     for (int32_t iMbx = 0; iMbx < m_iMbWidth; iMbx++) {
268 
269       const uint8_t kuiQp = rand() % 52;
270 
271       InitMe (kuiQp, m_uiMvdInterTableSize, m_uiMvdInterTableStride, m_pMvdCostTable, &sMe);
272       SetMvWithinIntegerMvRange (m_iMbWidth, m_iMbHeight, iMbx , iMby, m_iMvRange,
273                                  & (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
274 
275 
276       sMe.sMvp.iMvX = rand() % m_iMvRange;
277       sMe.sMvp.iMvY = rand() % m_iMvRange;
278       sMe.iCurMeBlockPixX = (iMbx << 4);
279       sMe.iCurMeBlockPixY = (iMby << 4);
280       sMe.pRefMb = pRef + sMe.iCurMeBlockPixX + sMe.iCurMeBlockPixY * m_iWidthExt;
281       sMe.pEncMb = m_pSrc + sMe.iCurMeBlockPixX + sMe.iCurMeBlockPixY * m_iWidth;
282       sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
283       sMe.pColoRefMb = sMe.pRefMb;
284       WelsMotionCrossSearch (&sFuncList, &sMe, &sSlice, m_iWidth, m_iWidthExt);
285       if ((WELS_ABS (sMe.sMv.iMvX) > m_iMvRange))
286         printf ("mvx = %d\n", sMe.sMv.iMvX);
287       ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvX) > m_iMvRange));
288       if ((WELS_ABS (sMe.sMv.iMvY) > m_iMvRange))
289         printf ("mvy = %d\n", sMe.sMv.iMvY);
290       ASSERT_TRUE (! (WELS_ABS (sMe.sMv.iMvY) > m_iMvRange));
291     }
292   }
293 }
DoLineTest(PLineFullSearchFunc func,bool vertical)294 void MotionEstimateTest::DoLineTest (PLineFullSearchFunc func, bool vertical) {
295   const int32_t kiMaxBlock16Sad = 72000;//a rough number
296   SWelsFuncPtrList sFuncList;
297   SWelsME sMe;
298 
299   const uint8_t kuiQp = rand() % 52;
300   InitMe (kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
301 
302   SMVUnitXY sTargetMv;
303   WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
304   WelsInitMeFunc (&sFuncList, WelsCPUFeatureDetect (NULL), 1);
305 
306   uint8_t* pRefPicCenter = m_pRefData + (m_iHeight / 2) * m_iWidth + (m_iWidth / 2);
307   sMe.iCurMeBlockPixX = (m_iWidth / 2);
308   sMe.iCurMeBlockPixY = (m_iHeight / 2);
309 
310   bool bDataGeneratorSucceed = false;
311   bool bFoundMatch = false;
312   int32_t iTryTimes = 100;
313 
314   if (vertical) {
315     sTargetMv.iMvX = 0;
316     sTargetMv.iMvY = -sMe.iCurMeBlockPixY + INTPEL_NEEDED_MARGIN + rand() % (m_iHeight - 16 - 2 * INTPEL_NEEDED_MARGIN);
317   } else {
318     sTargetMv.iMvX = -sMe.iCurMeBlockPixX + INTPEL_NEEDED_MARGIN + rand() % (m_iWidth - 16 - 2 * INTPEL_NEEDED_MARGIN);
319     sTargetMv.iMvY = 0;
320   }
321   bDataGeneratorSucceed = false;
322   bFoundMatch = false;
323   while (!bFoundMatch && (iTryTimes--) > 0) {
324     if (!YUVPixelDataGenerator (m_pRefData, m_iWidth, m_iHeight, m_iWidth))
325       continue;
326 
327     bDataGeneratorSucceed = true;
328     CopyTargetBlock (m_pSrcBlock, 16, sTargetMv, m_iWidth, pRefPicCenter);
329 
330     //clean the sMe status
331     sMe.uiBlockSize = rand() % 5;
332     sMe.pEncMb = m_pSrcBlock;
333     sMe.pRefMb = pRefPicCenter;
334     sMe.pColoRefMb = pRefPicCenter;
335     sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
336     sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
337     const int32_t iCurMeBlockPixX = sMe.iCurMeBlockPixX;
338     const int32_t iCurMeBlockQpelPixX = ((iCurMeBlockPixX) << 2);
339     const int32_t iCurMeBlockPixY = sMe.iCurMeBlockPixY;
340     const int32_t iCurMeBlockQpelPixY = ((iCurMeBlockPixY) << 2);
341     uint16_t* pMvdCostX = sMe.pMvdCost - iCurMeBlockQpelPixX - sMe.sMvp.iMvX; //do the offset here
342     uint16_t* pMvdCostY = sMe.pMvdCost - iCurMeBlockQpelPixY - sMe.sMvp.iMvY;
343     uint16_t* pMvdCost = vertical ? pMvdCostY : pMvdCostX;
344     int iSize = vertical ? m_iHeight : m_iWidth;
345 
346     //the last selection may be affected by MVDcost, that is when smaller MvY will be better
347     if (vertical) {
348       func (&sFuncList, &sMe,
349             pMvdCost,
350             m_iMaxSearchBlock, m_iWidth,
351             INTPEL_NEEDED_MARGIN - sMe.iCurMeBlockPixY,
352             iSize - INTPEL_NEEDED_MARGIN - 16 - sMe.iCurMeBlockPixY, vertical);
353       bFoundMatch = (sMe.sMv.iMvX == 0
354                      && (sMe.sMv.iMvY == sTargetMv.iMvY || abs (sMe.sMv.iMvY) < abs (sTargetMv.iMvY)));
355     } else {
356       func (&sFuncList, &sMe,
357             pMvdCost,
358             m_iMaxSearchBlock, m_iWidth,
359             INTPEL_NEEDED_MARGIN - sMe.iCurMeBlockPixX,
360             iSize - INTPEL_NEEDED_MARGIN - 16 - sMe.iCurMeBlockPixX, vertical);
361       bFoundMatch = (sMe.sMv.iMvY == 0
362                      && (sMe.sMv.iMvX == sTargetMv.iMvX || abs (sMe.sMv.iMvX) < abs (sTargetMv.iMvX)));
363     }
364     //printf("DoLineTest Target: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY);
365   }
366   if (bDataGeneratorSucceed) {
367     //if DataGenerator never succeed, there is no meaning to check iTryTimes
368     ASSERT_TRUE (iTryTimes > 0);
369     //it is possible that ref at differnt position is identical, but that should be under a low probability
370   }
371 }
372 
TEST_F(MotionEstimateTest,TestVerticalSearch)373 TEST_F (MotionEstimateTest, TestVerticalSearch) {
374   DoLineTest (LineFullSearch_c, true);
375 }
TEST_F(MotionEstimateTest,TestHorizontalSearch)376 TEST_F (MotionEstimateTest, TestHorizontalSearch) {
377   DoLineTest (LineFullSearch_c, false);
378 }
379 
380 #ifdef X86_ASM
TEST_F(MotionEstimateTest,TestVerticalSearch_SSE41)381 TEST_F (MotionEstimateTest, TestVerticalSearch_SSE41) {
382   int32_t iTmp = 1;
383   uint32_t uiCPUFlags = WelsCPUFeatureDetect (&iTmp);
384   if ((uiCPUFlags & WELS_CPU_SSE41) == 0) return ;
385 
386   DoLineTest (VerticalFullSearchUsingSSE41, true);
387 }
388 
TEST_F(MotionEstimateTest,TestHorizontalSearch_SSE41)389 TEST_F (MotionEstimateTest, TestHorizontalSearch_SSE41) {
390   int32_t iTmp = 1;
391   uint32_t uiCPUFlags = WelsCPUFeatureDetect (&iTmp);
392   if ((uiCPUFlags & WELS_CPU_SSE41) == 0) return ;
393 
394   DoLineTest (HorizontalFullSearchUsingSSE41, false);
395 }
396 #endif
397 
398 class FeatureMotionEstimateTest : public ::testing::Test {
399  public:
SetUp()400   virtual void SetUp() {
401     m_pRefData = NULL;
402     m_pSrcBlock = NULL;
403     m_pMvdCostTable = NULL;
404 
405     m_iWidth = 64;//size of search window
406     m_iHeight = 64;//size of search window
407     m_iMaxSearchBlock = 8;
408     m_uiMvdTableSize = (1 + (648 << 1));
409 
410     m_pMa = new CMemoryAlign (16);
411     ASSERT_TRUE (NULL != m_pMa);
412     m_pRefData = (uint8_t*)m_pMa->WelsMalloc (m_iWidth * m_iHeight * sizeof (uint8_t), "m_pRefData");
413     ASSERT_TRUE (NULL != m_pRefData);
414     m_pSrcBlock = (uint8_t*)m_pMa->WelsMalloc (m_iMaxSearchBlock * m_iMaxSearchBlock * sizeof (uint8_t), "m_pSrcBlock");
415     ASSERT_TRUE (NULL != m_pSrcBlock);
416     m_pMvdCostTable = (uint16_t*)m_pMa->WelsMalloc (52 * m_uiMvdTableSize * sizeof (uint16_t), "m_pMvdCostTable");
417     ASSERT_TRUE (NULL != m_pMvdCostTable);
418     m_pFeatureSearchPreparation = (SFeatureSearchPreparation*)m_pMa->WelsMalloc (sizeof (SFeatureSearchPreparation),
419                                   "m_pFeatureSearchPreparation");
420     ASSERT_TRUE (NULL != m_pFeatureSearchPreparation);
421     m_pScreenBlockFeatureStorage = (SScreenBlockFeatureStorage*)m_pMa->WelsMalloc (sizeof (SScreenBlockFeatureStorage),
422                                    "m_pScreenBlockFeatureStorage");
423     ASSERT_TRUE (NULL != m_pScreenBlockFeatureStorage);
424   }
TearDown()425   virtual void TearDown() {
426     if (m_pMa) {
427       if (m_pRefData) {
428         m_pMa->WelsFree (m_pRefData, "m_pRefData");
429         m_pRefData = NULL;
430       }
431       if (m_pSrcBlock) {
432         m_pMa->WelsFree (m_pSrcBlock, "m_pSrcBlock");
433         m_pSrcBlock = NULL;
434       }
435       if (m_pMvdCostTable) {
436         m_pMa->WelsFree (m_pMvdCostTable, "m_pMvdCostTable");
437         m_pMvdCostTable = NULL;
438       }
439 
440       if (m_pFeatureSearchPreparation) {
441         ReleaseFeatureSearchPreparation (m_pMa, m_pFeatureSearchPreparation->pFeatureOfBlock);
442         m_pMa->WelsFree (m_pFeatureSearchPreparation, "m_pFeatureSearchPreparation");
443         m_pFeatureSearchPreparation = NULL;
444       }
445       if (m_pScreenBlockFeatureStorage) {
446         ReleaseScreenBlockFeatureStorage (m_pMa, m_pScreenBlockFeatureStorage);
447         m_pMa->WelsFree (m_pScreenBlockFeatureStorage, "m_pScreenBlockFeatureStorage");
448         m_pScreenBlockFeatureStorage = NULL;
449       }
450       delete m_pMa;
451       m_pMa = NULL;
452     }
453   }
InitRefPicForMeTest(SPicture * pRefPic)454   void InitRefPicForMeTest (SPicture* pRefPic) {
455     pRefPic->pData[0] = m_pRefData;
456     pRefPic->iLineSize[0] = m_iWidth;
457     pRefPic->iFrameAverageQp = rand() % 52;
458     pRefPic->iWidthInPixel = m_iWidth;
459     pRefPic->iHeightInPixel = m_iHeight;
460   }
461  public:
462   CMemoryAlign* m_pMa;
463 
464   SFeatureSearchPreparation* m_pFeatureSearchPreparation;
465   SScreenBlockFeatureStorage* m_pScreenBlockFeatureStorage;
466 
467   uint8_t* m_pRefData;
468   uint8_t* m_pSrcBlock;
469   uint16_t* m_pMvdCostTable;
470   uint32_t m_uiMvdTableSize;
471 
472   int32_t m_iWidth;
473   int32_t m_iHeight;
474   int32_t m_iMaxSearchBlock;
475 };
476 
TEST_F(FeatureMotionEstimateTest,TestFeatureSearch)477 TEST_F (FeatureMotionEstimateTest, TestFeatureSearch) {
478   const int32_t kiMaxBlock16Sad = 72000;//a rough number
479   SWelsFuncPtrList sFuncList;
480   WelsInitSampleSadFunc (&sFuncList, 0); //test c functions
481   WelsInitMeFunc (&sFuncList, 0, true);
482 
483   SWelsME sMe;
484   const uint8_t kuiQp = rand() % 52;
485   InitMe (kuiQp, 648, m_uiMvdTableSize, m_pMvdCostTable, &sMe);
486   sMe.iCurMeBlockPixX = (m_iWidth / 2);
487   sMe.iCurMeBlockPixY = (m_iHeight / 2);
488   uint8_t* pRefPicCenter = m_pRefData + (m_iHeight / 2) * m_iWidth + (m_iWidth / 2);
489 
490   SPicture sRef;
491   InitRefPicForMeTest (&sRef);
492 
493   SSlice sSlice;
494   const int32_t kiSupposedPaddingLength = 16;
495   SetMvWithinIntegerMvRange (m_iWidth / 16 - kiSupposedPaddingLength, m_iHeight / 16 - kiSupposedPaddingLength,
496                              m_iWidth / 2 / 16, m_iHeight / 2 / 16, 508,
497                              & (sSlice.sMvStartMin), & (sSlice.sMvStartMax));
498   int32_t iReturn;
499   const int32_t kiNeedFeatureStorage = ME_DIA_CROSS_FME;
500   iReturn = RequestFeatureSearchPreparation (m_pMa, m_iWidth,  m_iHeight, kiNeedFeatureStorage,
501             m_pFeatureSearchPreparation);
502   ASSERT_TRUE (ENC_RETURN_SUCCESS == iReturn);
503   iReturn = RequestScreenBlockFeatureStorage (m_pMa, m_iWidth, m_iHeight, kiNeedFeatureStorage,
504             m_pScreenBlockFeatureStorage);
505   ASSERT_TRUE (ENC_RETURN_SUCCESS == iReturn);
506 
507   SMVUnitXY sTargetMv;
508   for (int i = sSlice.sMvStartMin.iMvX; i <= sSlice.sMvStartMax.iMvX; i++) {
509     for (int j = sSlice.sMvStartMin.iMvY; j <= sSlice.sMvStartMax.iMvY; j++) {
510       if (i == 0 || j == 0) continue; //exclude x=0 or y=0 since that will be skipped by FME
511 
512       bool bDataGeneratorSucceed = false;
513       bool bFoundMatch = false;
514 
515       if (!YUVPixelDataGenerator (m_pRefData, m_iWidth, m_iHeight, m_iWidth))
516         continue;
517       bDataGeneratorSucceed = true;
518 
519       sTargetMv.iMvX = i;
520       sTargetMv.iMvY = j;
521       CopyTargetBlock (m_pSrcBlock, m_iMaxSearchBlock, sTargetMv, m_iWidth, pRefPicCenter);
522 
523       //clean sMe status
524       sMe.uiBlockSize = BLOCK_8x8;
525       sMe.pEncMb = m_pSrcBlock;
526       sMe.pRefMb = pRefPicCenter;
527       sMe.pColoRefMb = pRefPicCenter;
528       sMe.sMv.iMvX = sMe.sMv.iMvY = 0;
529       sMe.uiSadCost = sMe.uiSatdCost = kiMaxBlock16Sad;
530 
531       //begin FME process
532       PerformFMEPreprocess (&sFuncList, &sRef, m_pFeatureSearchPreparation->pFeatureOfBlock,
533                             m_pScreenBlockFeatureStorage);
534       m_pScreenBlockFeatureStorage->uiSadCostThreshold[BLOCK_8x8] = UINT_MAX;//to avoid early skip
535       uint32_t uiMaxSearchPoint = INT_MAX;
536       SFeatureSearchIn sFeatureSearchIn = {0};
537       if (SetFeatureSearchIn (&sFuncList, sMe, &sSlice, m_pScreenBlockFeatureStorage,
538                               m_iMaxSearchBlock, m_iWidth,
539                               &sFeatureSearchIn)) {
540         MotionEstimateFeatureFullSearch (sFeatureSearchIn, uiMaxSearchPoint, &sMe);
541       }
542 
543       bool bMvMatch  = sMe.sMv.iMvX == sTargetMv.iMvX && sMe.sMv.iMvY == sTargetMv.iMvY;
544       bool bFeatureMatch =
545         (* (m_pScreenBlockFeatureStorage->pFeatureOfBlockPointer + (m_iHeight / 2 + sTargetMv.iMvY) * (m_iWidth - 8) +
546             (m_iWidth / 2 + sTargetMv.iMvX))
547          == * (m_pScreenBlockFeatureStorage->pFeatureOfBlockPointer + (m_iHeight / 2 + sMe.sMv.iMvY) * (m_iWidth - 8) +
548                (m_iWidth / 2 + sMe.sMv.iMvX)))
549         && ((sMe.pMvdCost[sMe.sMv.iMvY << 2] + sMe.pMvdCost[sMe.sMv.iMvX << 2]) <= (sMe.pMvdCost[sTargetMv.iMvY << 2] +
550             sMe.pMvdCost[sTargetMv.iMvX << 2]));
551 
552       //the last selection may be affected by MVDcost, that is when smaller Mv will be better
553       bFoundMatch = bMvMatch || bFeatureMatch;
554 
555       if (bDataGeneratorSucceed) {
556         //if DataGenerator never succeed, there is no meaning to check iTryTimes
557         if (!bFoundMatch) {
558           printf ("TestFeatureSearch Target: %d,%d, Result: %d,%d\n", sTargetMv.iMvX, sTargetMv.iMvY, sMe.sMv.iMvX, sMe.sMv.iMvY);
559         }
560         EXPECT_TRUE (bFoundMatch);
561       }
562     }
563   }
564 }
565 
566