shithub: openh264

Download patch

ref: d81dbeaa2e24cf7ad7154203145105463bdcc788
parent: 58966cb2e8d8c8a5f78c1acd7edc92bb99c65291
parent: cbbbe4cbae88dd49b1da5c1373882f52d1dcae2b
author: Licai Guo <licaguo@cisco.com>
date: Fri Mar 21 05:03:42 EDT 2014

Merge pull request #559 from huili2/UT_PredMv

add UT for PredMv in decoder

--- /dev/null
+++ b/test/DecUT_PredMv.cpp
@@ -1,0 +1,670 @@
+#include<gtest/gtest.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "../codec/decoder/core/inc/wels_common_basis.h"
+#include "../codec/decoder/core/inc/mem_align.h"
+#include "../codec/decoder/core/inc/mv_pred.h"
+#include "ls_defines.h"
+
+using namespace WelsDec;
+
+//Anchor functions
+#define REF_NOT_AVAIL    -2
+#define REF_NOT_IN_LIST  -1  //intra
+
+//cache element equal to 30
+const uint8_t g_kuiAnchorCache30ScanIdx[16] = { //mv or ref_index cache scan index, 4*4 block as basic unit
+  7,  8, 13, 14,
+  9, 10, 15, 16,
+  19, 20, 25, 26,
+  21, 22, 27, 28
+};
+
+typedef struct TagAnchorMvPred {
+  int16_t iMvArray[2][30][2];
+  int8_t iRefIdxArray[2][30];
+  int32_t iPartIdx;
+  int32_t iPartWidth;
+  int32_t iRef;
+  int16_t iMvp[2];
+} SAnchorMvPred;
+
+void AnchorPredMv (int16_t iMotionVector[LIST_A][30][MV_A], int8_t iRefIndex[LIST_A][30],
+                   int32_t iPartIdx, int32_t iPartWidth, int8_t iRef, int16_t iMVP[2]) {
+  const uint8_t kuiLeftIdx     = g_kuiAnchorCache30ScanIdx[iPartIdx] - 1;
+  const uint8_t kuiTopIdx      = g_kuiAnchorCache30ScanIdx[iPartIdx] - 6;
+  const uint8_t kuiRightTopIdx = kuiTopIdx + iPartWidth;
+  const uint8_t kuiLeftTopIdx  = kuiTopIdx - 1;
+  const int8_t kiLeftRef       = iRefIndex[0][kuiLeftIdx];
+  const int8_t kiTopRef        = iRefIndex[0][kuiTopIdx];
+  const int8_t kiRightTopRef   = iRefIndex[0][kuiRightTopIdx];
+  const int8_t kiLeftTopRef    = iRefIndex[0][kuiLeftTopIdx];
+  int8_t iDiagonalRef  = kiRightTopRef;
+  int8_t iMatchRef = 0;
+
+  int16_t iAMV[2], iBMV[2], iCMV[2];
+
+  * (int32_t*)iAMV = INTD32 (iMotionVector[0][kuiLeftIdx]);
+  * (int32_t*)iBMV = INTD32 (iMotionVector[0][kuiTopIdx]);
+  * (int32_t*)iCMV = INTD32 (iMotionVector[0][kuiRightTopIdx]);
+
+  if (REF_NOT_AVAIL == iDiagonalRef) {
+    iDiagonalRef = kiLeftTopRef;
+    * (int32_t*)iCMV = INTD32 (iMotionVector[0][kuiLeftTopIdx]);
+  }
+
+  iMatchRef = (iRef == kiLeftRef) + (iRef == kiTopRef) + (iRef == iDiagonalRef);
+
+  if ((REF_NOT_AVAIL == kiTopRef) && (REF_NOT_AVAIL == iDiagonalRef) && (kiLeftRef >= REF_NOT_IN_LIST)) {
+    ST32 (iMVP, LD32 (iAMV));
+    return;
+  }
+
+  if (1 == iMatchRef) {
+    if (iRef == kiLeftRef) {
+      ST32 (iMVP, LD32 (iAMV));
+    } else if (iRef == kiTopRef) {
+      ST32 (iMVP, LD32 (iBMV));
+    } else {
+      ST32 (iMVP, LD32 (iCMV));
+    }
+  } else {
+    iMVP[0] = WelsMedian (iAMV[0], iBMV[0], iCMV[0]);
+    iMVP[1] = WelsMedian (iAMV[1], iBMV[1], iCMV[1]);
+  }
+}
+
+void AnchorPredInter8x16Mv (int16_t iMotionVector[LIST_A][30][MV_A], int8_t iRefIndex[LIST_A][30],
+                            int32_t iPartIdx, int8_t iRef, int16_t iMVP[2]) {
+  if (0 == iPartIdx) {
+    const int8_t kiLeftRef = iRefIndex[0][6];
+    if (iRef == kiLeftRef) {
+      ST32 (iMVP, LD32 (&iMotionVector[0][6][0]));
+      return;
+    }
+  } else { // 4 == iPartIdx
+    int8_t iDiagonalRef = iRefIndex[0][5]; //top-right
+    int8_t index = 5;
+    if (REF_NOT_AVAIL == iDiagonalRef) {
+      iDiagonalRef = iRefIndex[0][2]; //top-left for 8*8 block(index 1)
+      index = 2;
+    }
+    if (iRef == iDiagonalRef) {
+      ST32 (iMVP, LD32 (&iMotionVector[0][index][0]));
+      return;
+    }
+  }
+
+  AnchorPredMv (iMotionVector, iRefIndex, iPartIdx, 2, iRef, iMVP);
+}
+
+void AnchorPredInter16x8Mv (int16_t iMotionVector[LIST_A][30][MV_A], int8_t iRefIndex[LIST_A][30],
+                            int32_t iPartIdx, int8_t iRef, int16_t iMVP[2]) {
+  if (0 == iPartIdx) {
+    const int8_t kiTopRef = iRefIndex[0][1];
+    if (iRef == kiTopRef) {
+      ST32 (iMVP, LD32 (&iMotionVector[0][1][0]));
+      return;
+    }
+  } else { // 8 == iPartIdx
+    const int8_t kiLeftRef = iRefIndex[0][18];
+    if (iRef == kiLeftRef) {
+      ST32 (iMVP, LD32 (&iMotionVector[0][18][0]));
+      return;
+    }
+  }
+
+  AnchorPredMv (iMotionVector, iRefIndex, iPartIdx, 4, iRef, iMVP);
+}
+
+
+//Ref functions in WelsDec
+//Input structure for test
+typedef struct TagWelsMvPred {
+  int16_t iMvArray[2][30][2];
+  int8_t iRefIdxArray[2][30];
+  int32_t iPartIdx;
+  int32_t iPartWidth;
+  int32_t iRef;
+  int16_t iMvp[2];
+} SWelsMvPred;
+
+//mok input data
+void AssignMvInputData (SAnchorMvPred* pAncMvPred) {
+  int32_t i, j, k;
+  srand ((uint32_t)time (NULL));
+  //fill MV data and refIdx
+  for (i = 0; i < 2; ++i) {
+    for (j = 0; j < 30; ++j) {
+      for (k = 0; k < 2; ++k) {
+        pAncMvPred->iMvArray[i][j][k] = (rand() - RAND_MAX / 2);
+      }
+      pAncMvPred->iRefIdxArray[i][j] = (rand() % 18) - 2; //-2 ~ 15. 8x8 may have different values, but it matters nothing
+    }
+  }
+}
+
+void CopyMvInputData (SAnchorMvPred* pDstMvPred, SWelsMvPred* pSrcMvPred) {
+  int32_t i, j, k;
+  //fill MV data and refIdx
+  for (i = 0; i < 2; ++i) {
+    for (j = 0; j < 30; ++j) {
+      for (k = 0; k < 2; ++k) {
+        pDstMvPred->iMvArray[i][j][k] = pSrcMvPred->iMvArray[i][j][k];
+      }
+      pDstMvPred->iRefIdxArray[i][j] = pSrcMvPred->iRefIdxArray[i][j];
+    }
+  }
+}
+
+#define INIT_MV_DATA \
+  AssignMvInputData (&sAncMvPred); \
+  CopyMvInputData (&sAncMvPred, &sWelsMvPred);
+
+#define TEST_MV_PRED \
+  AnchorPredMv (sAncMvPred.iMvArray,  sAncMvPred.iRefIdxArray, iIndex, iBlockWidth, iRef,  sAncMvPred.iMvp); \
+  PredMv (sWelsMvPred.iMvArray, sWelsMvPred.iRefIdxArray, iIndex, iBlockWidth, iRef, sWelsMvPred.iMvp); \
+  bOK = ((sAncMvPred.iMvp[0] == sWelsMvPred.iMvp[0]) && (sAncMvPred.iMvp[1] == sWelsMvPred.iMvp[1])); \
+  EXPECT_EQ (bOK, true);
+
+
+//TEST cases followed
+
+TEST (PredMvTest, PredMv) {
+  SWelsMvPred  sWelsMvPred;
+  SAnchorMvPred sAncMvPred;
+  int32_t i, iRef, iBlockWidth, iIndex;
+  const int32_t kiRandTime = 100;
+  bool bOK = true;
+
+  //test specific input: 16x16
+  iIndex = 0;
+  iBlockWidth = 4;
+  i = 0;
+  srand ((uint32_t)time (NULL));
+  while (i++ < kiRandTime) {
+    iRef = (rand() % 18) - 2; //-2~15
+    INIT_MV_DATA;
+    TEST_MV_PRED;
+  }
+  //test specific input: 16x8
+  iBlockWidth = 4;
+  i = 0;
+  while (i++ < kiRandTime) {
+    iIndex = (rand() & 1) << 3; //0,8
+    iRef = (rand() % 18) - 2; //-2~15
+    INIT_MV_DATA;
+    TEST_MV_PRED;
+  }
+  //test specific input: 8x16
+  iBlockWidth = 2;
+  i = 0;
+  while (i++ < kiRandTime) {
+    iIndex = (rand() & 1) << 2; //0,4
+    iRef = (rand() % 18) - 2; //-2~15
+    INIT_MV_DATA;
+    TEST_MV_PRED;
+  }
+  //test specific input: 8x8
+  iBlockWidth = 2;
+  i = 0;
+  while (i++ < kiRandTime) {
+    iIndex = (rand() & 3) << 2; //0,4,8,12
+    iRef = (rand() % 18) - 2; //-2~15
+    INIT_MV_DATA;
+    TEST_MV_PRED;
+  }
+  //test specific input: 4x4
+  iBlockWidth = 1;
+  i = 0;
+  while (i++ < kiRandTime) {
+    iIndex = rand() & 0x0f; //0~15
+    iRef = (rand() % 18) - 2; //-2~15
+    INIT_MV_DATA;
+    TEST_MV_PRED;
+  }
+} //TEST PredMv
+
+
+TEST (PredMvTest, PredInter16x8Mv) {
+  SWelsMvPred  sWelsMvPred;
+  SAnchorMvPred sAncMvPred;
+  int32_t i, iRef, iIndex;
+  const int32_t kiRandTime = 100;
+  bool bOK = true;
+
+  i = 0;
+  srand ((uint32_t)time (NULL));
+  while (i++ < kiRandTime) {
+    iIndex = (rand() & 1) << 3; //0, 8
+    iRef = (rand() % 18) - 2; //-2~15
+    INIT_MV_DATA;
+    AnchorPredInter16x8Mv (sAncMvPred.iMvArray,  sAncMvPred.iRefIdxArray, iIndex, iRef,  sAncMvPred.iMvp);
+    PredInter16x8Mv (sWelsMvPred.iMvArray, sWelsMvPred.iRefIdxArray, iIndex, iRef, sWelsMvPred.iMvp);
+    bOK = ((sAncMvPred.iMvp[0] == sWelsMvPred.iMvp[0]) && (sAncMvPred.iMvp[1] == sWelsMvPred.iMvp[1]));
+    EXPECT_EQ (bOK, true);
+  }
+} //TEST PredInter16x8Mv
+
+TEST (PredMvTest, PredInter8x16Mv) {
+  SWelsMvPred  sWelsMvPred;
+  SAnchorMvPred sAncMvPred;
+  int32_t i, iRef, iIndex;
+  const int32_t kiRandTime = 100;
+  bool bOK = true;
+
+  i = 0;
+  srand ((uint32_t)time (NULL));
+  while (i++ < kiRandTime) {
+    iIndex = (rand() & 1) << 2; //0, 4
+    iRef = (rand() % 18) - 2; //-2~15
+    INIT_MV_DATA;
+    AnchorPredInter8x16Mv (sAncMvPred.iMvArray,  sAncMvPred.iRefIdxArray, iIndex, iRef,  sAncMvPred.iMvp);
+    PredInter8x16Mv (sWelsMvPred.iMvArray, sWelsMvPred.iRefIdxArray, iIndex, iRef, sWelsMvPred.iMvp);
+    bOK = ((sAncMvPred.iMvp[0] == sWelsMvPred.iMvp[0]) && (sAncMvPred.iMvp[1] == sWelsMvPred.iMvp[1]));
+    EXPECT_EQ (bOK, true);
+  }
+} //TEST PredInter16x8Mv
+
+void AnchorPredPSkipMvFromNeighbor (PDqLayer pCurLayer, int16_t iMvp[2]) {
+  bool bTopAvail, bLeftTopAvail, bRightTopAvail, bLeftAvail;
+
+  int32_t iCurSliceIdc, iTopSliceIdc, iLeftTopSliceIdc, iRightTopSliceIdc, iLeftSliceIdc;
+  int32_t iLeftTopType, iRightTopType, iTopType, iLeftType;
+  int32_t iCurX, iCurY, iCurXy, iLeftXy, iTopXy, iLeftTopXy, iRightTopXy;
+
+  int8_t iLeftRef;
+  int8_t iTopRef;
+  int8_t iRightTopRef;
+  int8_t iLeftTopRef;
+  int8_t iDiagonalRef;
+  int8_t iMatchRef;
+  int16_t iMvA[2], iMvB[2], iMvC[2], iMvD[2];
+
+  iCurXy = pCurLayer->iMbXyIndex;
+  iCurX  = pCurLayer->iMbX;
+  iCurY  = pCurLayer->iMbY;
+  iCurSliceIdc = pCurLayer->pSliceIdc[iCurXy];
+
+  if (iCurX != 0) {
+    iLeftXy = iCurXy - 1;
+    iLeftSliceIdc = pCurLayer->pSliceIdc[iLeftXy];
+    bLeftAvail = (iLeftSliceIdc == iCurSliceIdc);
+  } else {
+    bLeftAvail = 0;
+    bLeftTopAvail = 0;
+  }
+
+  if (iCurY != 0) {
+    iTopXy = iCurXy - pCurLayer->iMbWidth;
+    iTopSliceIdc = pCurLayer->pSliceIdc[iTopXy];
+    bTopAvail = (iTopSliceIdc == iCurSliceIdc);
+    if (iCurX != 0) {
+      iLeftTopXy = iTopXy - 1;
+      iLeftTopSliceIdc = pCurLayer->pSliceIdc[iLeftTopXy];
+      bLeftTopAvail = (iLeftTopSliceIdc  == iCurSliceIdc);
+    } else {
+      bLeftTopAvail = 0;
+    }
+    if (iCurX != (pCurLayer->iMbWidth - 1)) {
+      iRightTopXy = iTopXy + 1;
+      iRightTopSliceIdc = pCurLayer->pSliceIdc[iRightTopXy];
+      bRightTopAvail = (iRightTopSliceIdc == iCurSliceIdc);
+    } else {
+      bRightTopAvail = 0;
+    }
+  } else {
+    bTopAvail = 0;
+    bLeftTopAvail = 0;
+    bRightTopAvail = 0;
+  }
+
+  iLeftType = ((iCurX != 0 && bLeftAvail) ? pCurLayer->pMbType[iLeftXy] : 0);
+  iTopType = ((iCurY != 0 && bTopAvail) ? pCurLayer->pMbType[iTopXy] : 0);
+  iLeftTopType = ((iCurX != 0 && iCurY != 0 && bLeftTopAvail)
+                  ? pCurLayer->pMbType[iLeftTopXy] : 0);
+  iRightTopType = ((iCurX != pCurLayer->iMbWidth - 1 && iCurY != 0 && bRightTopAvail)
+                   ? pCurLayer->pMbType[iRightTopXy] : 0);
+
+  /*get neb mv&iRefIdxArray*/
+  /*left*/
+  if (bLeftAvail && IS_INTER (iLeftType)) {
+    ST32 (iMvA, LD32 (pCurLayer->pMv[0][iLeftXy][3]));
+    iLeftRef = pCurLayer->pRefIndex[0][iLeftXy][3];
+  } else {
+    ST32 (iMvA, 0);
+    if (0 == bLeftAvail) { //not available
+      iLeftRef = REF_NOT_AVAIL;
+    } else { //available but is intra mb type
+      iLeftRef = REF_NOT_IN_LIST;
+    }
+  }
+  if (REF_NOT_AVAIL == iLeftRef ||
+      (0 == iLeftRef && 0 == * (int32_t*)iMvA)) {
+    ST32 (iMvp, 0);
+    return;
+  }
+
+  /*top*/
+  if (bTopAvail && IS_INTER (iTopType)) {
+    ST32 (iMvB, LD32 (pCurLayer->pMv[0][iTopXy][12]));
+    iTopRef = pCurLayer->pRefIndex[0][iTopXy][12];
+  } else {
+    ST32 (iMvB, 0);
+    if (0 == bTopAvail) { //not available
+      iTopRef = REF_NOT_AVAIL;
+    } else { //available but is intra mb type
+      iTopRef = REF_NOT_IN_LIST;
+    }
+  }
+  if (REF_NOT_AVAIL == iTopRef ||
+      (0 == iTopRef  && 0 == * (int32_t*)iMvB)) {
+    ST32 (iMvp, 0);
+    return;
+  }
+
+  /*right_top*/
+  if (bRightTopAvail && IS_INTER (iRightTopType)) {
+    ST32 (iMvC, LD32 (pCurLayer->pMv[0][iRightTopXy][12]));
+    iRightTopRef = pCurLayer->pRefIndex[0][iRightTopXy][12];
+  } else {
+    ST32 (iMvC, 0);
+    if (0 == bRightTopAvail) { //not available
+      iRightTopRef = REF_NOT_AVAIL;
+    } else { //available but is intra mb type
+      iRightTopRef = REF_NOT_IN_LIST;
+    }
+  }
+
+  /*left_top*/
+  if (bLeftTopAvail && IS_INTER (iLeftTopType)) {
+    ST32 (iMvD, LD32 (pCurLayer->pMv[0][iLeftTopXy][15]));
+    iLeftTopRef = pCurLayer->pRefIndex[0][iLeftTopXy][15];
+  } else {
+    ST32 (iMvD, 0);
+    if (0 == bLeftTopAvail) { //not available
+      iLeftTopRef = REF_NOT_AVAIL;
+    } else { //available but is intra mb type
+      iLeftTopRef = REF_NOT_IN_LIST;
+    }
+  }
+
+  iDiagonalRef = iRightTopRef;
+  if (REF_NOT_AVAIL == iDiagonalRef) {
+    iDiagonalRef = iLeftTopRef;
+    * (int32_t*)iMvC = * (int32_t*)iMvD;
+  }
+
+  if (REF_NOT_AVAIL == iTopRef && REF_NOT_AVAIL == iDiagonalRef && iLeftRef >= REF_NOT_IN_LIST) {
+    ST32 (iMvp, LD32 (iMvA));
+    return;
+  }
+
+  iMatchRef = (0 == iLeftRef) + (0 == iTopRef) + (0 == iDiagonalRef);
+  if (1 == iMatchRef) {
+    if (0 == iLeftRef) {
+      ST32 (iMvp, LD32 (iMvA));
+    } else if (0 == iTopRef) {
+      ST32 (iMvp, LD32 (iMvB));
+    } else {
+      ST32 (iMvp, LD32 (iMvC));
+    }
+  } else {
+    iMvp[0] = WelsMedian (iMvA[0], iMvB[0], iMvC[0]);
+    iMvp[1] = WelsMedian (iMvA[1], iMvB[1], iMvC[1]);
+  }
+}
+
+
+
+int32_t AllocLayerData (PDqLayer pDqLayer) {
+
+  pDqLayer->pSliceIdc = (int32_t*) WelsMalloc (pDqLayer->iMbWidth * pDqLayer->iMbHeight * sizeof (int32_t),
+                        "pDqLayer->pSliceIdc");
+  if (pDqLayer->pSliceIdc == NULL)
+    return 1;
+
+  pDqLayer->pMbType = (int8_t*) WelsMalloc (pDqLayer->iMbWidth * pDqLayer->iMbHeight * sizeof (int8_t),
+                      "pDqLayer->pMbType");
+  if (pDqLayer->pMbType == NULL)
+    return 1;
+
+  pDqLayer->pMv[0] = (int16_t (*)[MB_BLOCK4x4_NUM][MV_A]) WelsMalloc (pDqLayer->iMbWidth * pDqLayer->iMbHeight * sizeof (
+                       int16_t) * MV_A * MB_BLOCK4x4_NUM, "pDqLayer->pMv");
+  if (pDqLayer->pMv[0] == NULL)
+    return 1;
+
+  pDqLayer->pRefIndex[0] = (int8_t (*)[MB_BLOCK4x4_NUM]) WelsMalloc (pDqLayer->iMbWidth * pDqLayer->iMbHeight * sizeof (
+                             int8_t) * MB_BLOCK4x4_NUM, "pDqLayer->pRefIndex");
+  if (pDqLayer->pRefIndex[0] == NULL)
+    return 1;
+
+  return 0;
+}
+
+int32_t FreeLayerData (PDqLayer pDqLayer) {
+
+  if (pDqLayer->pSliceIdc != NULL) {
+    WelsFree (pDqLayer->pSliceIdc, "pDqLayer->pSliceIdc");
+    pDqLayer->pSliceIdc = NULL;
+  }
+
+  if (pDqLayer->pMbType != NULL) {
+    WelsFree (pDqLayer->pMbType, "pDqLayer->pMbType");
+    pDqLayer->pMbType = NULL;
+  }
+
+  if (pDqLayer->pMv[0] != NULL) {
+    WelsFree (pDqLayer->pMv[0], "pDqlayer->pMv[0]");
+    pDqLayer->pMv[0] = NULL;
+  }
+
+  if (pDqLayer->pRefIndex[0] != NULL) {
+    WelsFree (pDqLayer->pRefIndex[0], "pDqlayer->pRefIndex[0]");
+    pDqLayer->pRefIndex[0] = NULL;
+  }
+
+  return 0;
+}
+
+void InitRandomLayerSliceIdc (PDqLayer pDqLayer) {
+  srand ((uint32_t)time (NULL));
+  int32_t i = 0;
+  int32_t iTotalMbNum = pDqLayer->iMbWidth * pDqLayer->iMbHeight;
+  int32_t iMbFirstSliceEnd = rand() % (iTotalMbNum - 1); //assure 2 slices
+  for (i = 0; i <= iMbFirstSliceEnd; ++i) {
+    pDqLayer->pSliceIdc[i] = 0; //to keep simple value here
+  }
+  for (; i < iTotalMbNum; ++i) {
+    pDqLayer->pSliceIdc[i] = 1; //to keep simple value here
+  }
+}
+
+void InitRandomLayerMbType (PDqLayer pDqLayer) {
+  srand ((uint32_t)time (NULL));
+  for (int32_t i = 0; i < pDqLayer->iMbWidth * pDqLayer->iMbHeight; ++i) {
+    pDqLayer->pMbType[i] = (rand() & 0x0f) + 1; //1 ~ 16
+  }
+}
+
+void InitRandomLayerMvData (PDqLayer pDqLayer) {
+  srand ((uint32_t)time (NULL));
+  for (int32_t i = 0; i < pDqLayer->iMbWidth * pDqLayer->iMbHeight; ++i) {
+    for (int32_t j = 0; j < MB_BLOCK4x4_NUM; ++j) {
+      for (int32_t k = 0; k < MV_A; ++k) {
+        pDqLayer->pMv[0][i][j][k] = (rand() - RAND_MAX / 2);
+      }
+    }
+  }
+}
+
+void InitRandomLayerRefIdxData (PDqLayer pDqLayer) {
+  srand ((uint32_t)time (NULL));
+  for (int32_t i = 0; i < pDqLayer->iMbWidth * pDqLayer->iMbHeight; ++i) {
+    for (int32_t j = 0; j < MB_BLOCK4x4_NUM; ++j) {
+      pDqLayer->pRefIndex[0][i][j] = (rand() % 18 - 2); //-2 ~ 15
+    }
+  }
+}
+
+void InitRandomLayerData (PDqLayer pDqLayer) {
+  InitRandomLayerSliceIdc (pDqLayer);
+  InitRandomLayerMbType (pDqLayer);
+  InitRandomLayerMvData (pDqLayer);
+  InitRandomLayerRefIdxData (pDqLayer);
+}
+
+#define TEST_SKIP_MV_PRED \
+  PredPSkipMvFromNeighbor (&sDqLayer, iWelsMvp); \
+  bOK = ((iWelsMvp[0] == iAncMvp[0]) && (iWelsMvp[1] == iAncMvp[1])); \
+  EXPECT_EQ (bOK, true);
+
+TEST (PredMvTest, PredSkipMvFromNeighbor) {
+  const int32_t kiRandTime = 100;
+  bool bOK = true;
+  SDqLayer sDqLayer;
+  int16_t iAncMvp[2], iWelsMvp[2];
+
+  memset (&sDqLayer, 0, sizeof (SDqLayer));
+  //Assume the input data as 352x288 size
+  //allocate the data
+  sDqLayer.iMbWidth = 11;
+  sDqLayer.iMbHeight = 9;
+  if (AllocLayerData (&sDqLayer)) { //memory allocate failed
+    FreeLayerData (&sDqLayer);
+    return;
+  }
+  InitRandomLayerData (&sDqLayer); //init MV data, as it would not affect the following logic test
+
+#define CURR_MB_IDX (sDqLayer.iMbXyIndex)
+#define LEFT_MB_IDX (sDqLayer.iMbXyIndex - 1)
+#define LEFT_MB_BLK 3
+#define TOP_MB_IDX (sDqLayer.iMbXyIndex - sDqLayer.iMbWidth)
+#define TOP_MB_BLK 12
+#define LEFT_TOP_MB_IDX (sDqLayer.iMbXyIndex - sDqLayer.iMbWidth - 1)
+#define LEFT_TOP_MB_BLK 15
+#define RIGHT_TOP_MB_IDX (sDqLayer.iMbXyIndex - sDqLayer.iMbWidth + 1)
+#define RIGHT_TOP_MB_BLK 12
+
+  //CASE 1: test MB [0,0], expect mvp = (0,0)
+  sDqLayer.iMbX = 0;
+  sDqLayer.iMbY = 0;
+  sDqLayer.iMbXyIndex = sDqLayer.iMbY * sDqLayer.iMbWidth + sDqLayer.iMbX;
+  iAncMvp[0] = iAncMvp[1] = 0; //expect anchor result to 0
+  TEST_SKIP_MV_PRED;
+  //CASE 2: test MB [ANY, 0], expect mvp = (0,0)
+  sDqLayer.iMbX = rand() % sDqLayer.iMbWidth;
+  sDqLayer.iMbY = 0;
+  sDqLayer.iMbXyIndex = sDqLayer.iMbY * sDqLayer.iMbWidth + sDqLayer.iMbX;
+  iAncMvp[0] = iAncMvp[1] = 0; //expect anchor result to 0
+  TEST_SKIP_MV_PRED;
+  //CASE 3: test MB [0, ANY], expect mvp = (0,0)
+  sDqLayer.iMbX = 0;
+  sDqLayer.iMbY = rand() % sDqLayer.iMbHeight;
+  sDqLayer.iMbXyIndex = sDqLayer.iMbY * sDqLayer.iMbWidth + sDqLayer.iMbX;
+  iAncMvp[0] = iAncMvp[1] = 0; //expect anchor result to 0
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1: test MB [RIGHT_SIDE, ANY]
+  sDqLayer.iMbX = sDqLayer.iMbWidth - 1;
+  sDqLayer.iMbY = rand() % (sDqLayer.iMbHeight - 1) + 1; //not equal to 0
+  sDqLayer.iMbXyIndex = sDqLayer.iMbY * sDqLayer.iMbWidth + sDqLayer.iMbX;
+  //CASE 4.1.1: same slice_idc, assume = 0
+  memset (sDqLayer.pSliceIdc, 0, sDqLayer.iMbWidth * sDqLayer.iMbHeight * sizeof (int32_t));
+  //CASE 4.1.1.1: ALL P modes
+  memset (sDqLayer.pMbType, MB_TYPE_16x16, sDqLayer.iMbWidth * sDqLayer.iMbHeight * sizeof (int8_t));
+  //CASE 4.1.1.1.1: ref_idx = 0, left MV = 0, top MV != 0, expect mvp = (0,0)
+  memset (sDqLayer.pRefIndex[0], 0, sDqLayer.iMbWidth * sDqLayer.iMbHeight * MB_BLOCK4x4_NUM * sizeof (int8_t));
+  InitRandomLayerMvData (&sDqLayer); //reset Mv data
+  sDqLayer.pMv[0][LEFT_MB_IDX][LEFT_MB_BLK][0] = sDqLayer.pMv[0][LEFT_MB_IDX][LEFT_MB_BLK][1] = 0; //left_mv = 0
+  sDqLayer.pMv[0][ TOP_MB_IDX][ TOP_MB_BLK][0] = sDqLayer.pMv[0][ TOP_MB_IDX][ TOP_MB_BLK][1] = 1; //top_mv != 0
+  iAncMvp[0] = iAncMvp[1] = 0; //expect anchor result to 0
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1.1.1.2: ref_idx = 0, left MV != 0, top MV = 0, expect mvp = (0,0)
+  memset (sDqLayer.pRefIndex[0], 0, sDqLayer.iMbWidth * sDqLayer.iMbHeight * MB_BLOCK4x4_NUM * sizeof (int8_t));
+  InitRandomLayerMvData (&sDqLayer); //reset Mv data
+  sDqLayer.pMv[0][LEFT_MB_IDX][LEFT_MB_BLK][0] = sDqLayer.pMv[0][LEFT_MB_IDX][LEFT_MB_BLK][1] = 1; //left_mv != 0
+  sDqLayer.pMv[0][ TOP_MB_IDX][ TOP_MB_BLK][0] = sDqLayer.pMv[0][ TOP_MB_IDX][ TOP_MB_BLK][1] = 0; //top_mv = 0
+  iAncMvp[0] = iAncMvp[1] = 0; //expect anchor result to 0
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1.1.1.3: ref_idx top = 0, others = 1, expect mvp = top mv
+  InitRandomLayerMvData (&sDqLayer); //reset Mv data
+  sDqLayer.pRefIndex[0][ TOP_MB_IDX][ TOP_MB_BLK] = 0; //top ref_idx = 0
+  sDqLayer.pRefIndex[0][LEFT_MB_IDX][LEFT_MB_BLK] = 1; //left ref_idx = 1
+  sDqLayer.pRefIndex[0][LEFT_TOP_MB_IDX][LEFT_TOP_MB_BLK] = 1; //left_top ref_idx = 1
+  iAncMvp[0] = sDqLayer.pMv[0][TOP_MB_IDX][TOP_MB_BLK][0];
+  iAncMvp[1] = sDqLayer.pMv[0][TOP_MB_IDX][TOP_MB_BLK][1];
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1.1.1.4: ref_idx left = 0, others = 1, expect mvp = left mv
+  sDqLayer.pRefIndex[0][ TOP_MB_IDX][ TOP_MB_BLK] = 1; //top ref_idx = 1
+  sDqLayer.pRefIndex[0][LEFT_MB_IDX][LEFT_MB_BLK] = 0; //left ref_idx = 0
+  sDqLayer.pRefIndex[0][LEFT_TOP_MB_IDX][LEFT_TOP_MB_BLK] = 1; //left_top ref_idx = 1
+  iAncMvp[0] = sDqLayer.pMv[0][LEFT_MB_IDX][LEFT_MB_BLK][0];
+  iAncMvp[1] = sDqLayer.pMv[0][LEFT_MB_IDX][LEFT_MB_BLK][1];
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1.1.2: All I
+  memset (sDqLayer.pMbType, MB_TYPE_INTRA16x16, sDqLayer.iMbWidth * sDqLayer.iMbHeight * sizeof (int8_t));
+  //CASE 4.1.1.2.1: left P, expect mvp = left mv
+  sDqLayer.pMbType[LEFT_MB_IDX] = MB_TYPE_16x16; //left P
+  iAncMvp[0] = sDqLayer.pMv[0][LEFT_MB_IDX][LEFT_MB_BLK][0];
+  iAncMvp[1] = sDqLayer.pMv[0][LEFT_MB_IDX][LEFT_MB_BLK][1];
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1.1.3: only top P, top ref_idx = 0, expect mvp = top mv
+  memset (sDqLayer.pMbType, MB_TYPE_INTRA16x16, sDqLayer.iMbWidth * sDqLayer.iMbHeight * sizeof (int8_t)); // All I MB
+  memset (sDqLayer.pRefIndex[0], 1, sDqLayer.iMbWidth * sDqLayer.iMbHeight * MB_BLOCK4x4_NUM * sizeof (
+            int8_t)); // All ref_idx = 1
+  sDqLayer.pMbType[TOP_MB_IDX] = MB_TYPE_16x16; //top P
+  sDqLayer.pRefIndex[0][TOP_MB_IDX][TOP_MB_BLK] = 0; //top ref_idx = 0
+  iAncMvp[0] = sDqLayer.pMv[0][TOP_MB_IDX][TOP_MB_BLK][0];
+  iAncMvp[1] = sDqLayer.pMv[0][TOP_MB_IDX][TOP_MB_BLK][1];
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1.1.4: only left_top P, left_top ref_idx = 0, expect mvp = 0
+  sDqLayer.iMbX = (rand() % (sDqLayer.iMbWidth - 2)) + 1; //1 ~ (mb_width - 2)
+  sDqLayer.iMbY = (rand() % (sDqLayer.iMbHeight - 2)) + 1; //1 ~ (mb_height - 2)
+  sDqLayer.iMbXyIndex = sDqLayer.iMbY * sDqLayer.iMbWidth + sDqLayer.iMbX;
+  memset (sDqLayer.pMbType, MB_TYPE_INTRA16x16, sDqLayer.iMbWidth * sDqLayer.iMbHeight * sizeof (int8_t)); // All I MB
+  memset (sDqLayer.pRefIndex[0], 1, sDqLayer.iMbWidth * sDqLayer.iMbHeight * MB_BLOCK4x4_NUM * sizeof (
+            int8_t)); // All ref_idx = 1
+  sDqLayer.pMbType[LEFT_TOP_MB_IDX] = MB_TYPE_16x16; //top P
+  sDqLayer.pRefIndex[0][LEFT_TOP_MB_IDX][LEFT_TOP_MB_BLK] = 0; //top ref_idx = 0
+  iAncMvp[0] = iAncMvp[1] = 0; //expect anchor result to 0
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1.1.5: only right_top P, right_top ref_idx = 0, expect mvp = right_top mv
+  sDqLayer.iMbX = (rand() % (sDqLayer.iMbWidth - 2)) + 1; //1 ~ (mb_width - 2)
+  sDqLayer.iMbY = (rand() % (sDqLayer.iMbHeight - 2)) + 1; //1 ~ (mb_height - 2)
+  sDqLayer.iMbXyIndex = sDqLayer.iMbY * sDqLayer.iMbWidth + sDqLayer.iMbX;
+  memset (sDqLayer.pMbType, MB_TYPE_INTRA16x16, sDqLayer.iMbWidth * sDqLayer.iMbHeight * sizeof (int8_t)); // All I MB
+  memset (sDqLayer.pRefIndex[0], 1, sDqLayer.iMbWidth * sDqLayer.iMbHeight * MB_BLOCK4x4_NUM * sizeof (
+            int8_t)); // All ref_idx = 1
+  sDqLayer.pMbType[RIGHT_TOP_MB_IDX] = MB_TYPE_16x16; //top P
+  sDqLayer.pRefIndex[0][RIGHT_TOP_MB_IDX][RIGHT_TOP_MB_BLK] = 0; //top ref_idx = 0
+  iAncMvp[0] = sDqLayer.pMv[0][RIGHT_TOP_MB_IDX][RIGHT_TOP_MB_BLK][0];
+  iAncMvp[1] = sDqLayer.pMv[0][RIGHT_TOP_MB_IDX][RIGHT_TOP_MB_BLK][1];
+  TEST_SKIP_MV_PRED;
+  //CASE 4.1.2: different neighbor slice idc for all P and ref_idx = 0, expect mvp = 0
+  memset (sDqLayer.pMbType, MB_TYPE_16x16, sDqLayer.iMbWidth * sDqLayer.iMbHeight * sizeof (int8_t)); // All I MB
+  memset (sDqLayer.pRefIndex[0], 0, sDqLayer.iMbWidth * sDqLayer.iMbHeight * MB_BLOCK4x4_NUM * sizeof (
+            int8_t)); // All ref_idx = 1
+  sDqLayer.iMbX = (rand() % (sDqLayer.iMbWidth - 2)) + 1; //1 ~ (mb_width - 2)
+  sDqLayer.iMbY = (rand() % (sDqLayer.iMbHeight - 2)) + 1; //1 ~ (mb_height - 2)
+  sDqLayer.iMbXyIndex = sDqLayer.iMbY * sDqLayer.iMbWidth + sDqLayer.iMbX;
+  sDqLayer.pSliceIdc[CURR_MB_IDX] = 5;
+  sDqLayer.pSliceIdc[LEFT_MB_IDX] = 0;
+  sDqLayer.pSliceIdc[TOP_MB_IDX] = 1;
+  sDqLayer.pSliceIdc[LEFT_TOP_MB_IDX] = 2;
+  sDqLayer.pSliceIdc[RIGHT_TOP_MB_IDX] = 3;
+  iAncMvp[0] = iAncMvp[1] = 0;
+  TEST_SKIP_MV_PRED;
+
+  //add new specific tests here
+
+  //normal tests
+  int32_t i = 0;
+  while (i++ < kiRandTime) {
+    InitRandomLayerData (&sDqLayer); //init MV data, as it would not affect the following logic test
+    AnchorPredPSkipMvFromNeighbor (&sDqLayer, iAncMvp);
+    TEST_SKIP_MV_PRED;
+  }
+
+  FreeLayerData (&sDqLayer);
+}
--- a/test/targets.mk
+++ b/test/targets.mk
@@ -9,6 +9,7 @@
 	$(CODEC_UNITTEST_SRCDIR)/DecUT_ExpandPicture.cpp\
 	$(CODEC_UNITTEST_SRCDIR)/DecUT_IdctResAddPred.cpp\
 	$(CODEC_UNITTEST_SRCDIR)/DecUT_IntraPrediction.cpp\
+	$(CODEC_UNITTEST_SRCDIR)/DecUT_PredMv.cpp\
 	$(CODEC_UNITTEST_SRCDIR)/encoder_test.cpp\
 	$(CODEC_UNITTEST_SRCDIR)/EncUT_MemoryAlloc.cpp\
 	$(CODEC_UNITTEST_SRCDIR)/EncUT_MotionEstimate.cpp\