diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 830fc4bf4fb6c05537ba28249664864f9b42e981..fa49d881ed4604a2f3560f9c37831ea72429f3b7 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -294,6 +294,16 @@ static const int AFFINE_MAX_NUM_V2 =                                2; ///< max
 static const int AFFINE_MAX_NUM_COMB =                             12; ///< max number of combined motion candidates
 static const int AFFINE_MIN_BLOCK_SIZE =                            4; ///< Minimum affine MC block size
 
+
+#if JVET_L0054_MMVD
+static const int MMVD_REFINE_STEP =                                 8; ///< max number of distance step
+static const int MMVD_MAX_REFINE_NUM =                              (MMVD_REFINE_STEP * 4); ///< max number of candidate from a base candidate
+static const int MMVD_BASE_MV_NUM =                                 2; ///< max number of base candidate
+static const int MMVD_ADD_NUM =                                     (MMVD_MAX_REFINE_NUM * MMVD_BASE_MV_NUM);///< total number of mmvd candidate
+static const int MMVD_MRG_MAX_RD_NUM =                              MRG_MAX_NUM_CANDS;
+static const int MMVD_MRG_MAX_RD_BUF_NUM =                          (MMVD_MRG_MAX_RD_NUM + 1);///< increase buffer size by 1
+#endif
+
 #if JVET_L0274
 static const int MAX_NUM_REG_BINS_4x4SUBBLOCK =                    32; ///< max number of context-coded bins (incl. gt2 bins) per 4x4 subblock
 static const int MAX_NUM_GT2_BINS_4x4SUBBLOCK =                     4; ///< max number of gt2 bins per 4x4 subblock
diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index 9f41baa2f13ebd3fb2c9912b3ec24a1ba834bde7..bbdc6f2ed935169ad7e1581664170a3c3d7bbd88 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -365,6 +365,9 @@ void MergeCtx::setMergeInfo( PredictionUnit& pu, int candIdx )
   CHECK( candIdx >= numValidMergeCand, "Merge candidate does not exist" );
 
   pu.mergeFlag               = true;
+#if JVET_L0054_MMVD
+  pu.mmvdMergeFlag = false;
+#endif
   pu.interDir                = interDirNeighbours[candIdx];
   pu.mergeIdx                = candIdx;
   pu.mergeType               = mrgTypeNeighbours[candIdx];
@@ -384,3 +387,209 @@ void MergeCtx::setMergeInfo( PredictionUnit& pu, int candIdx )
 #endif
 
 }
+#if JVET_L0054_MMVD
+void MergeCtx::setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx)
+{
+  const Slice &slice = *pu.cs->slice;
+  const int mvShift = VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
+  const int refMvdCands[8] = { 1 << mvShift , 2 << mvShift , 4 << mvShift , 8 << mvShift , 16 << mvShift , 32 << mvShift,  64 << mvShift , 128 << mvShift };
+  int fPosGroup = 0;
+  int fPosBaseIdx = 0;
+  int fPosStep = 0;
+  int tempIdx = 0;
+  int fPosPosition = 0;
+  Mv tempMv[2];
+
+  tempIdx = candIdx;
+  fPosGroup = tempIdx / (MMVD_BASE_MV_NUM * MMVD_MAX_REFINE_NUM);
+  tempIdx = tempIdx - fPosGroup * (MMVD_BASE_MV_NUM * MMVD_MAX_REFINE_NUM);
+  fPosBaseIdx = tempIdx / MMVD_MAX_REFINE_NUM;
+  tempIdx = tempIdx - fPosBaseIdx * (MMVD_MAX_REFINE_NUM);
+  fPosStep = tempIdx / 4;
+  fPosPosition = tempIdx - fPosStep * (4);
+
+  const int offset = refMvdCands[fPosStep];
+#if !REMOVE_MV_ADAPT_PREC
+  const int highPrecList0 = mmvdBaseMv[fPosBaseIdx][0].mv.highPrec;
+  const int highPrecList1 = mmvdBaseMv[fPosBaseIdx][1].mv.highPrec;
+#endif
+  const int refList0 = mmvdBaseMv[fPosBaseIdx][0].refIdx;
+  const int refList1 = mmvdBaseMv[fPosBaseIdx][1].refIdx;
+
+  if ((refList0 != -1) && (refList1 != -1))
+  {
+    const int poc0 = slice.getRefPOC(REF_PIC_LIST_0, refList0);
+    const int poc1 = slice.getRefPOC(REF_PIC_LIST_1, refList1);
+    const int currPoc = slice.getPOC();
+    int refSign = 1;
+
+    if ((poc0 - currPoc) * (currPoc - poc1) > 0)
+    {
+      refSign = -1;
+    }
+#if REMOVE_MV_ADAPT_PREC
+    if (fPosPosition == 0)
+    {
+      tempMv[0] = Mv(offset, 0);
+      tempMv[1] = Mv(offset * refSign, 0);
+    }
+    else if (fPosPosition == 1)
+    {
+      tempMv[0] = Mv(-offset, 0);
+      tempMv[1] = Mv(-offset * refSign, 0);
+    }
+    else if (fPosPosition == 2)
+    {
+      tempMv[0] = Mv(0, offset);
+      tempMv[1] = Mv(0, offset * refSign);
+    }
+    else
+    {
+      tempMv[0] = Mv(0, -offset);
+      tempMv[1] = Mv(0, -offset * refSign);
+    }
+#else
+    if (fPosPosition == 0)
+    {
+      tempMv[0] = Mv(offset, 0, highPrecList0);
+      tempMv[1] = Mv(offset * refSign, 0, highPrecList1);
+    }
+    else if (fPosPosition == 1)
+    {
+      tempMv[0] = Mv(-offset, 0, highPrecList0);
+      tempMv[1] = Mv(-offset * refSign, 0, highPrecList1);
+    }
+    else if (fPosPosition == 2)
+    {
+      tempMv[0] = Mv(0, offset, highPrecList0);
+      tempMv[1] = Mv(0, offset * refSign, highPrecList1);
+    }
+    else
+    {
+      tempMv[0] = Mv(0, -offset, highPrecList0);
+      tempMv[1] = Mv(0, -offset * refSign, highPrecList1);
+    }
+#endif
+    if (abs(poc1 - currPoc) > abs(poc0 - currPoc))
+    {
+      const int scale = PU::getDistScaleFactor(currPoc, poc0, currPoc, poc1);
+      if (scale != 4096)
+      {
+        tempMv[0] = tempMv[0].scaleMv(scale);
+      }
+    }
+    else if (abs(poc1 - currPoc) < abs(poc0 - currPoc))
+    {
+      const int scale = PU::getDistScaleFactor(currPoc, poc1, currPoc, poc0);
+      if (scale != 4096)
+      {
+        tempMv[1] = tempMv[1].scaleMv(scale);
+      }
+    }
+
+    pu.interDir = 3;
+    pu.mv[REF_PIC_LIST_0] = mmvdBaseMv[fPosBaseIdx][0].mv + tempMv[0];
+    pu.refIdx[REF_PIC_LIST_0] = refList0;
+    pu.mv[REF_PIC_LIST_1] = mmvdBaseMv[fPosBaseIdx][1].mv + tempMv[1];
+    pu.refIdx[REF_PIC_LIST_1] = refList1;
+  }
+  else if (refList0 != -1)
+  {
+#if REMOVE_MV_ADAPT_PREC
+    if (fPosPosition == 0)
+    {
+      tempMv[0] = Mv(offset, 0);
+    }
+    else if (fPosPosition == 1)
+    {
+      tempMv[0] = Mv(-offset, 0);
+    }
+    else if (fPosPosition == 2)
+    {
+      tempMv[0] = Mv(0, offset);
+    }
+    else
+    {
+      tempMv[0] = Mv(0, -offset);
+    }
+#else
+    if (fPosPosition == 0)
+    {
+      tempMv[0] = Mv(offset, 0, highPrecList0);
+    }
+    else if (fPosPosition == 1)
+    {
+      tempMv[0] = Mv(-offset, 0, highPrecList0);
+    }
+    else if (fPosPosition == 2)
+    {
+      tempMv[0] = Mv(0, offset, highPrecList0);
+    }
+    else
+    {
+      tempMv[0] = Mv(0, -offset, highPrecList0);
+    }
+#endif
+    pu.interDir = 1;
+    pu.mv[REF_PIC_LIST_0] = mmvdBaseMv[fPosBaseIdx][0].mv + tempMv[0];
+    pu.refIdx[REF_PIC_LIST_0] = refList0;
+    pu.mv[REF_PIC_LIST_1] = Mv(0, 0);
+    pu.refIdx[REF_PIC_LIST_1] = -1;
+  }
+  else if (refList1 != -1)
+  {
+#if REMOVE_MV_ADAPT_PREC
+    if (fPosPosition == 0)
+    {
+      tempMv[1] = Mv(offset, 0);
+    }
+    else if (fPosPosition == 1)
+    {
+      tempMv[1] = Mv(-offset, 0);
+    }
+    else if (fPosPosition == 2)
+    {
+      tempMv[1] = Mv(0, offset);
+    }
+    else
+    {
+      tempMv[1] = Mv(0, -offset);
+    }
+#else
+    if (fPosPosition == 0)
+    {
+      tempMv[1] = Mv(offset, 0, highPrecList1);
+    }
+    else if (fPosPosition == 1)
+    {
+      tempMv[1] = Mv(-offset, 0, highPrecList1);
+    }
+    else if (fPosPosition == 2)
+    {
+      tempMv[1] = Mv(0, offset, highPrecList1);
+    }
+    else
+    {
+      tempMv[1] = Mv(0, -offset, highPrecList1);
+    }
+#endif
+    pu.interDir = 2;
+    pu.mv[REF_PIC_LIST_0] = Mv(0, 0);
+    pu.refIdx[REF_PIC_LIST_0] = -1;
+    pu.mv[REF_PIC_LIST_1] = mmvdBaseMv[fPosBaseIdx][1].mv + tempMv[1];
+    pu.refIdx[REF_PIC_LIST_1] = refList1;
+  }
+
+  pu.mmvdMergeFlag = true;
+  pu.mmvdMergeIdx = candIdx;
+  pu.mergeFlag = true;
+  pu.mergeIdx = candIdx;
+  pu.mergeType = MRG_TYPE_DEFAULT_N;
+  pu.mvd[REF_PIC_LIST_0] = Mv();
+  pu.mvd[REF_PIC_LIST_1] = Mv();
+  pu.mvpIdx[REF_PIC_LIST_0] = NOT_VALID;
+  pu.mvpIdx[REF_PIC_LIST_1] = NOT_VALID;
+  pu.mvpNum[REF_PIC_LIST_0] = NOT_VALID;
+  pu.mvpNum[REF_PIC_LIST_1] = NOT_VALID;
+}
+#endif
\ No newline at end of file
diff --git a/source/Lib/CommonLib/ContextModelling.h b/source/Lib/CommonLib/ContextModelling.h
index 58c8d8e5898b9222b1fa7529b6654b597c06ffed..f98d40a0190883c0de652daa87671730f511eef7 100644
--- a/source/Lib/CommonLib/ContextModelling.h
+++ b/source/Lib/CommonLib/ContextModelling.h
@@ -309,6 +309,10 @@ public:
 
   MotionBuf     subPuMvpMiBuf;
   MotionBuf     subPuMvpExtMiBuf;
+#if JVET_L0054_MMVD
+  MvField mmvdBaseMv[MMVD_BASE_MV_NUM][2];
+  void setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx);
+#endif
   void setMergeInfo( PredictionUnit& pu, int candIdx );
 };
 
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 434e66e89c9de59b81126a4def2fbfdc28370c46..36f6118eaf38ddb98d48e980b40da6665321ee1c 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -313,7 +313,28 @@ const CtxSet ContextSetCfg::MergeIdx = ContextSetCfg::addCtxSet
   {  CNU, CNU, CNU, CNU, CNU,},
 #endif
 });
+#if JVET_L0054_MMVD
+const CtxSet ContextSetCfg::MmvdFlag = ContextSetCfg::addCtxSet
+({
+  { 151, },
+  { CNU, },
+  { CNU, },
+  });
 
+const CtxSet ContextSetCfg::MmvdMergeIdx = ContextSetCfg::addCtxSet
+({
+  { CNU, },
+  { CNU, },
+  { CNU, },
+  });
+
+const CtxSet ContextSetCfg::MmvdStepMvpIdx = ContextSetCfg::addCtxSet
+({
+  { 184, },
+  { CNU, },
+  { CNU, },
+  });
+#endif
 const CtxSet ContextSetCfg::PartSize = ContextSetCfg::addCtxSet
 ({
   {  154, 139, 154, 154,},
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index 3e4679c06bd64c00f0f7611e8ca52e739902a135..3e08409e3475ef600e486ccfed9c35e40144ed1b 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -166,6 +166,11 @@ public:
   static const CtxSet   DeltaQP;
   static const CtxSet   InterDir;
   static const CtxSet   RefPic;
+#if JVET_L0054_MMVD
+  static const CtxSet   MmvdFlag;
+  static const CtxSet   MmvdMergeIdx;
+  static const CtxSet   MmvdStepMvpIdx;
+#endif
   static const CtxSet   AffineFlag;
   static const CtxSet   AffineType;
   static const CtxSet   Mvd;
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 2fbbae285a1e3412f81905c1fca0711294f72b51..556890451adf06f598e58442291034d6b267c3f8 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -61,6 +61,7 @@
 #define JVET_L0191_LM_WO_LMS                              1 // NO LMS regression. min/max are used instead
 
 #define JVET_L0090_PAIR_AVG                               1 // Add pairwise average candidates, replace HEVC combined candidates
+#define JVET_L0054_MMVD                                   1
 
 #define JVET_L0392_ALF_INIT_STATE                         1
 
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 3c21d11bcce7c067ba06c957b8eaa08bc5b18272..e81e2170b05dc2fff3268b1370cf4ae7a49cde36 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -253,6 +253,9 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other )
   mtDepth           = other.mtDepth;
   splitSeries       = other.splitSeries;
   skip              = other.skip;
+#if JVET_L0054_MMVD
+  mmvdSkip = other.mmvdSkip;
+#endif
   affine            = other.affine;
   affineType        = other.affineType;
   transQuantBypass  = other.transQuantBypass;
@@ -284,6 +287,9 @@ void CodingUnit::initData()
   mtDepth           = 0;
   splitSeries       = 0;
   skip              = false;
+#if JVET_L0054_MMVD
+  mmvdSkip = false;
+#endif
   affine            = false;
   affineType        = 0;
   transQuantBypass  = false;
@@ -321,6 +327,10 @@ void PredictionUnit::initData()
   // inter data
   mergeFlag   = false;
   mergeIdx    = MAX_UCHAR;
+#if JVET_L0054_MMVD
+  mmvdMergeFlag = false;
+  mmvdMergeIdx = MAX_UINT;
+#endif
   interDir    = MAX_UCHAR;
   mergeType   = MRG_TYPE_DEFAULT_N;
   for (uint32_t i = 0; i < NUM_REF_PIC_LIST_01; i++)
@@ -351,6 +361,10 @@ PredictionUnit& PredictionUnit::operator=(const InterPredictionData& predData)
 {
   mergeFlag   = predData.mergeFlag;
   mergeIdx    = predData.mergeIdx;
+#if JVET_L0054_MMVD
+  mmvdMergeFlag = predData.mmvdMergeFlag;
+  mmvdMergeIdx = predData.mmvdMergeIdx;
+#endif
   interDir    = predData.interDir;
   mergeType   = predData.mergeType;
   for (uint32_t i = 0; i < NUM_REF_PIC_LIST_01; i++)
@@ -378,6 +392,10 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other )
 
   mergeFlag   = other.mergeFlag;
   mergeIdx    = other.mergeIdx;
+#if JVET_L0054_MMVD
+  mmvdMergeFlag = other.mmvdMergeFlag;
+  mmvdMergeIdx = other.mmvdMergeIdx;
+#endif
   interDir    = other.interDir;
   mergeType   = other.mergeType;
   for (uint32_t i = 0; i < NUM_REF_PIC_LIST_01; i++)
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 93924a20065a649ddfe4e80adaf1b4553766616c..083a2e36eefe2257e9b01c77f96670b4ff5ed906 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -295,6 +295,9 @@ struct CodingUnit : public UnitArea
   int8_t          qp;
   SplitSeries    splitSeries;
   bool           skip;
+#if JVET_L0054_MMVD
+  bool           mmvdSkip;
+#endif
   bool           affine;
   int            affineType;
   bool           transQuantBypass;
@@ -349,6 +352,10 @@ struct InterPredictionData
 {
   bool      mergeFlag;
   uint8_t     mergeIdx;
+#if JVET_L0054_MMVD
+  bool           mmvdMergeFlag;
+  uint32_t       mmvdMergeIdx;
+#endif
   uint8_t     interDir;
   uint8_t     mvpIdx  [NUM_REF_PIC_LIST_01];
   uint8_t     mvpNum  [NUM_REF_PIC_LIST_01];
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index ad55e2ec379c61087bbfd6a9fdb37ad57391b434..a511f85701136115f5055e4cf660a21aaf97c571 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -494,8 +494,11 @@ uint32_t PU::getFinalIntraMode( const PredictionUnit &pu, const ChannelType &chT
   return uiIntraMode;
 }
 
-
-void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx, const int& mrgCandIdx )
+void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
+#if JVET_L0054_MMVD
+  int mmvdList,
+#endif
+  const int& mrgCandIdx )
 {
   const CodingStructure &cs  = *pu.cs;
   const Slice &slice         = *pu.cs->slice;
@@ -723,7 +726,11 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx, co
     bool bMrgIdxMatchATMVPCan = ( mrgCandIdx == cnt );
     bool tmpLICFlag           = false;
 
-    isAvailableSubPu = cs.sps->getSpsNext().getUseATMVP() && getInterMergeSubPuMvpCand( pu, mrgCtx, tmpLICFlag, cnt 
+    isAvailableSubPu = cs.sps->getSpsNext().getUseATMVP() &&     
+      getInterMergeSubPuMvpCand( pu, mrgCtx, tmpLICFlag, cnt 
+#if JVET_L0054_MMVD
+        , mmvdList
+#endif
     );
 
     if( isAvailableSubPu )
@@ -1117,7 +1124,78 @@ static int xGetDistScaleFactor(const int &iCurrPOC, const int &iCurrRefPOC, cons
     return iScale;
   }
 }
+#if JVET_L0054_MMVD
+int PU::getDistScaleFactor(const int &currPOC, const int &currRefPOC, const int &colPOC, const int &colRefPOC)
+{
+  return xGetDistScaleFactor(currPOC, currRefPOC, colPOC, colRefPOC);
+}
+
+void PU::getInterMMVDMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const int& mrgCandIdx)
+{
+  int refIdxList0, refIdxList1;
+  int k;
+  int currBaseNum = 0;
+  const uint16_t maxNumMergeCand = mrgCtx.numValidMergeCand;
+
+#if !REMOVE_MV_ADAPT_PREC
+  if (pu.cu->slice->getSPS()->getSpsNext().getUseHighPrecMv())
+  {
+    for (k = 0; k < maxNumMergeCand; k++)
+    {
+      if (mrgCtx.mrgTypeNeighbours[k] == MRG_TYPE_DEFAULT_N)
+      {
+        if ((mrgCtx.mvFieldNeighbours[(k << 1)].mv.highPrec == false) && (mrgCtx.mvFieldNeighbours[(k << 1)].refIdx >= 0))
+        {
+          mrgCtx.mvFieldNeighbours[(k << 1)].mv.setHighPrec();
+        }
+        if ((mrgCtx.mvFieldNeighbours[(k << 1) + 1].mv.highPrec == false) && (mrgCtx.mvFieldNeighbours[(k << 1) + 1].refIdx >= 0))
+        {
+          mrgCtx.mvFieldNeighbours[(k << 1) + 1].mv.setHighPrec();
+        }
+      }
+    }
+  }
+#endif
+  for (k = 0; k < maxNumMergeCand; k++)
+  {
+    if (mrgCtx.mrgTypeNeighbours[k] == MRG_TYPE_DEFAULT_N)
+    {
+      refIdxList0 = mrgCtx.mvFieldNeighbours[(k << 1)].refIdx;
+      refIdxList1 = mrgCtx.mvFieldNeighbours[(k << 1) + 1].refIdx;
+
+      if ((refIdxList0 >= 0) && (refIdxList1 >= 0))
+      {
+        mrgCtx.mmvdBaseMv[currBaseNum][0] = mrgCtx.mvFieldNeighbours[(k << 1)];
+        mrgCtx.mmvdBaseMv[currBaseNum][1] = mrgCtx.mvFieldNeighbours[(k << 1) + 1];
+      }
+      else if (refIdxList0 >= 0)
+      {
+        mrgCtx.mmvdBaseMv[currBaseNum][0] = mrgCtx.mvFieldNeighbours[(k << 1)];
+        mrgCtx.mmvdBaseMv[currBaseNum][1] = MvField(Mv(0, 0), -1);
+      }
+      else if (refIdxList1 >= 0)
+      {
+        mrgCtx.mmvdBaseMv[currBaseNum][0] = MvField(Mv(0, 0), -1);
+        mrgCtx.mmvdBaseMv[currBaseNum][1] = mrgCtx.mvFieldNeighbours[(k << 1) + 1];
+      }
 
+      currBaseNum++;
+
+      if (currBaseNum == MMVD_BASE_MV_NUM)
+        break;
+    }
+  }
+
+  if (currBaseNum < MMVD_BASE_MV_NUM)
+  {
+    for (k = currBaseNum; k < MMVD_BASE_MV_NUM; k++)
+    {
+      mrgCtx.mmvdBaseMv[k][0] = MvField(Mv(0, 0), 0);
+      mrgCtx.mmvdBaseMv[k][0] = MvField(Mv(0, 0), 0);
+    }
+  }
+}
+#endif
 bool PU::getColocatedMVP(const PredictionUnit &pu, const RefPicList &eRefPicList, const Position &_pos, Mv& rcMv, const int &refIdx )
 {
   // don't perform MV compression when generally disabled or subPuMvp is used
@@ -2287,6 +2365,9 @@ void clipColBlkMv(int& mvX, int& mvY, const PredictionUnit& pu)
 #endif
 
 bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, bool& LICFlag, const int count
+#if JVET_L0054_MMVD
+  , int mmvdList
+#endif
 )
 {
   const Slice   &slice = *pu.cs->slice;
@@ -2412,7 +2493,10 @@ bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, b
   {
     return false;
   }
-  
+#if JVET_L0054_MMVD
+  if (mmvdList != 1)
+  {
+#endif
 #if JVET_L0257_ATMVP_COLBLK_CLIP
   int xOff = (puWidth >> 1) + tempX;
   int yOff = (puHeight >> 1) + tempY;
@@ -2487,7 +2571,9 @@ bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, b
       mb.subBuf(g_miScaling.scale(Position{ x, y } -pu.lumaPos()), g_miScaling.scale(Size(puWidth, puHeight))).fill(mi);
       }
     }
-
+#if JVET_L0054_MMVD
+  }
+#endif
   return true;
   }
 
@@ -2617,7 +2703,11 @@ void PU::applyImv( PredictionUnit& pu, MergeCtx &mrgCtx, InterPrediction *interP
   {
     // this function is never called for merge
     THROW("unexpected");
-    PU::getInterMergeCandidates ( pu, mrgCtx );
+    PU::getInterMergeCandidates ( pu, mrgCtx 
+#if JVET_L0054_MMVD
+      , 0
+#endif
+    );
     PU::restrictBiPredMergeCands( pu, mrgCtx );
 
     mrgCtx.setMergeInfo( pu, pu.mergeIdx );
@@ -2710,7 +2800,11 @@ void CU::resetMVDandMV2Int( CodingUnit& cu, InterPrediction *interPred )
     }
     else
     {
-        PU::getInterMergeCandidates ( pu, mrgCtx );
+        PU::getInterMergeCandidates ( pu, mrgCtx 
+#if JVET_L0054_MMVD
+          , 0
+#endif
+        );
         PU::restrictBiPredMergeCands( pu, mrgCtx );
 
         mrgCtx.setMergeInfo( pu, pu.mergeIdx );
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 3247170c6f357a977fdc39d16be213adee1a28ee..84a6e3992f44a1832f9772fddb285c0438948748 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -109,8 +109,15 @@ namespace PU
   int  getIntraMPMs(const PredictionUnit &pu, unsigned *mpm, const ChannelType &channelType = CHANNEL_TYPE_LUMA);
   void getIntraChromaCandModes        (const PredictionUnit &pu, unsigned modeList[NUM_CHROMA_MODE]);
   uint32_t getFinalIntraMode              (const PredictionUnit &pu, const ChannelType &chType);
-
-  void getInterMergeCandidates        (const PredictionUnit &pu, MergeCtx& mrgCtx, const int& mrgCandIdx = -1 );
+  void getInterMergeCandidates        (const PredictionUnit &pu, MergeCtx& mrgCtx,
+#if JVET_L0054_MMVD
+    int mmvdList,
+#endif
+    const int& mrgCandIdx = -1 );
+#if JVET_L0054_MMVD
+  void getInterMMVDMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const int& mrgCandIdx = -1);
+  int getDistScaleFactor(const int &currPOC, const int &currRefPOC, const int &colPOC, const int &colRefPOC);
+#endif 
   bool isDiffMER                      (const PredictionUnit &pu, const PredictionUnit &pu2);
   bool getColocatedMVP                (const PredictionUnit &pu, const RefPicList &eRefPicList, const Position &pos, Mv& rcMv, const int &refIdx);
   void fillMvpCand                    (      PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AMVPInfo &amvpInfo );
@@ -135,6 +142,9 @@ namespace PU
 #endif 
   );
   bool getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx &mrgCtx, bool& LICFlag, const int count
+#if JVET_L0054_MMVD
+    , int mmvdList
+#endif
   );
   bool getInterMergeSubPuRecurCand(const PredictionUnit &pu, MergeCtx &mrgCtx, const int count);
   bool isBiPredFromDifferentDir       (const PredictionUnit &pu);
@@ -178,7 +188,11 @@ namespace TU
 uint32_t getCtuAddr        (const Position& pos, const PreCalcValues &pcv);
 
 template<typename T, size_t N>
+#if JVET_L0054_MMVD
+uint32_t updateCandList(T uiMode, double uiCost, static_vector<T, N>& candModeList, static_vector<double, N>& candCostList, size_t uiFastCandNum = N, int* iserttPos = nullptr)
+#else
 uint32_t updateCandList( T uiMode, double uiCost, static_vector<T, N>& candModeList, static_vector<double, N>& candCostList, size_t uiFastCandNum = N )
+#endif
 {
   CHECK( std::min( uiFastCandNum, candModeList.size() ) != std::min( uiFastCandNum, candCostList.size() ), "Sizes do not match!" );
   CHECK( uiFastCandNum > candModeList.capacity(), "The vector is to small to hold all the candidates!" );
@@ -201,15 +215,32 @@ uint32_t updateCandList( T uiMode, double uiCost, static_vector<T, N>& candModeL
     }
     candModeList[currSize - shift] = uiMode;
     candCostList[currSize - shift] = uiCost;
+#if JVET_L0054_MMVD
+    if (iserttPos != nullptr)
+    {
+      *iserttPos = int(currSize - shift);
+    }
+#endif
     return 1;
   }
   else if( currSize < uiFastCandNum )
   {
     candModeList.insert( candModeList.end() - shift, uiMode );
     candCostList.insert( candCostList.end() - shift, uiCost );
+#if JVET_L0054_MMVD
+    if (iserttPos != nullptr)
+    {
+      *iserttPos = int(candModeList.size() - shift - 1);
+    }
+#endif
     return 1;
   }
-
+#if JVET_L0054_MMVD
+  if (iserttPos != nullptr)
+  {
+    *iserttPos = -1;
+  }
+#endif
   return 0;
 }
 
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index f6c572efa44731ca674f93ba6942086e9b755c45..b58966e8de83e770f99d1db2bf36c9c2bbb01419 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -757,6 +757,11 @@ void CABACReader::cu_skip_flag( CodingUnit& cu )
 
   if( skip )
   {
+#if JVET_L0054_MMVD
+    unsigned mmvdSkip = m_BinDecoder.decodeBin(Ctx::MmvdFlag(0));
+    cu.mmvdSkip = mmvdSkip;
+    DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_cu_skip_flag() ctx=%d mmvd_skip=%d\n", 0, mmvdSkip ? 1 : 0);
+#endif
     cu.skip     = true;
     cu.rootCbf  = false;
     cu.predMode = MODE_INTER;
@@ -1155,6 +1160,13 @@ void CABACReader::prediction_unit( PredictionUnit& pu, MergeCtx& mrgCtx )
   if( pu.mergeFlag )
   {
     affine_flag  ( *pu.cu );
+#if JVET_L0054_MMVD
+    if (pu.mmvdMergeFlag)
+    {
+      mmvd_merge_idx(pu);
+    }
+    else
+#endif
     merge_data   ( pu );
   }
   else
@@ -1236,6 +1248,12 @@ void CABACReader::affine_flag( CodingUnit& cu )
   {
     return;
   }
+#if JVET_L0054_MMVD
+  if (cu.firstPU->mergeFlag && (cu.firstPU->mmvdMergeFlag || cu.mmvdSkip))
+  {
+    return;
+  }
+#endif
 
   CHECK( !cu.cs->pcv->rectCUs && cu.lumaSize().width != cu.lumaSize().height, "CU width and height are not equal for QTBT off." );
 
@@ -1265,6 +1283,13 @@ void CABACReader::merge_flag( PredictionUnit& pu )
   pu.mergeFlag = ( m_BinDecoder.decodeBin( Ctx::MergeFlag() ) );
 
   DTRACE( g_trace_ctx, D_SYNTAX, "merge_flag() merge=%d pos=(%d,%d) size=%dx%d\n", pu.mergeFlag ? 1 : 0, pu.lumaPos().x, pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height );
+#if JVET_L0054_MMVD
+  if (pu.mergeFlag)
+  {
+    pu.mmvdMergeFlag = (m_BinDecoder.decodeBin(Ctx::MmvdFlag(0)));
+    DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_merge_flag() mmvd_merge=%d pos=(%d,%d) size=%dx%d\n", pu.mmvdMergeFlag ? 1 : 0, pu.lumaPos().x, pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height);
+  }
+#endif
 }
 
 
@@ -1274,7 +1299,13 @@ void CABACReader::merge_data( PredictionUnit& pu )
   {
     return;
   }
-
+#if JVET_L0054_MMVD
+  if (pu.cu->mmvdSkip)
+  {
+    mmvd_merge_idx(pu);
+  }
+  else
+#endif
   merge_idx( pu );
 }
 
@@ -1319,6 +1350,76 @@ void CABACReader::merge_idx( PredictionUnit& pu )
   DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() merge_idx=%d\n", pu.mergeIdx );
 }
 
+#if JVET_L0054_MMVD
+void CABACReader::mmvd_merge_idx(PredictionUnit& pu)
+{
+  RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET(STATS__CABAC_BITS__MERGE_INDEX);
+  int var0, var1, var2;
+  int dir0 = 0;
+  int var = 0;
+  int mvpIdx = 0;
+
+  pu.mmvdMergeIdx = 0;
+
+  mvpIdx = (var + dir0)*(MMVD_MAX_REFINE_NUM*MMVD_BASE_MV_NUM);
+
+  int numCandminus1_base = MMVD_BASE_MV_NUM - 1;
+  var0 = 0;
+  if (numCandminus1_base > 0)
+  {
+    if (m_BinDecoder.decodeBin(Ctx::MmvdMergeIdx()))
+    {
+      var0++;
+      for (; var0 < numCandminus1_base; var0++)
+      {
+        if (!m_BinDecoder.decodeBinEP())
+        {
+          break;
+        }
+      }
+    }
+  }
+  DTRACE(g_trace_ctx, D_SYNTAX, "base_mvp_idx() base_mvp_idx=%d\n", var0);
+  int numCandminus1_step = MMVD_REFINE_STEP - 1;
+  var1 = 0;
+  if (numCandminus1_step > 0)
+  {
+    if (m_BinDecoder.decodeBin(Ctx::MmvdStepMvpIdx()))
+    {
+      var1++;
+      for (; var1 < numCandminus1_step; var1++)
+      {
+        if (!m_BinDecoder.decodeBinEP())
+        {
+          break;
+        }
+      }
+    }
+  }
+  DTRACE(g_trace_ctx, D_SYNTAX, "MmvdStepMvpIdx() MmvdStepMvpIdx=%d\n", var1);
+  var2 = 0;
+  if (m_BinDecoder.decodeBinEP())
+  {
+    var2 += 2;
+    if (m_BinDecoder.decodeBinEP())
+    {
+      var2 += 1;
+    }
+  }
+  else
+  {
+    var2 += 0;
+    if (m_BinDecoder.decodeBinEP())
+    {
+      var2 += 1;
+    }
+  }
+  DTRACE(g_trace_ctx, D_SYNTAX, "pos() pos=%d\n", var2);
+  mvpIdx += (var0 * MMVD_MAX_REFINE_NUM + var1 * 4 + var2);
+  pu.mmvdMergeIdx = mvpIdx;
+  DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_merge_idx() mmvd_merge_idx=%d\n", pu.mmvdMergeIdx);
+}
+#endif
 
 void CABACReader::inter_pred_idc( PredictionUnit& pu )
 {
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index fd194650c2275ac547e9cff453b7c7f04d167b64..fa5236476e45f3ebea59d0f230602df17a3a83bd 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -97,6 +97,9 @@ public:
   void        merge_data                ( PredictionUnit&               pu );
   void        affine_flag               ( CodingUnit&                   cu );
   void        merge_idx                 ( PredictionUnit&               pu );
+#if JVET_L0054_MMVD
+  void        mmvd_merge_idx(PredictionUnit&               pu);
+#endif
   void        imv_mode                  ( CodingUnit&                   cu,     MergeCtx&       mrgCtx );
   void        inter_pred_idc            ( PredictionUnit&               pu );
   void        ref_idx                   ( PredictionUnit&               pu,     RefPicList      eRefList );
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 8fb1c75ba89f278c1a621440ce9e8fd16f45bade..3b2672079e57eff9c93d995b4fb601d9f0352b97 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -414,6 +414,63 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
 
     if( pu.mergeFlag )
     {
+#if JVET_L0054_MMVD
+      if (pu.mmvdMergeFlag || pu.cu->mmvdSkip)
+      {
+        if (pu.cs->sps->getSpsNext().getUseSubPuMvp())
+        {
+          Size bufSize = g_miScaling.scale(pu.lumaSize());
+          mrgCtx.subPuMvpMiBuf = MotionBuf(m_SubPuMiBuf, bufSize);
+        }
+
+        if (cu.cs->pps->getLog2ParallelMergeLevelMinus2() && cu.partSize != SIZE_2Nx2N && cu.lumaSize().width <= 8)
+        {
+          if (!mrgCtx.hasMergedCandList)
+          {
+            // temporarily set size to 2Nx2N
+            PartSize                 tmpPS = SIZE_2Nx2N;
+            PredictionUnit           tmpPU = pu;
+            static_cast<UnitArea&> (tmpPU) = cu;
+            std::swap(tmpPS, cu.partSize);
+#if JVET_L0054_MMVD
+            int   fPosBaseIdx = pu.mmvdMergeIdx / MMVD_MAX_REFINE_NUM;
+            PU::getInterMergeCandidates(tmpPU, mrgCtx, 1, fPosBaseIdx + 1);
+#else
+            PU::getInterMergeCandidates(tmpPU, mrgCtx, 255);
+#endif
+            PU::getInterMMVDMergeCandidates(tmpPU, mrgCtx,
+              pu.mmvdMergeIdx
+            );
+            std::swap(tmpPS, cu.partSize);
+            mrgCtx.hasMergedCandList = true;
+          }
+        }
+        else
+        {
+#if JVET_L0054_MMVD
+          int   fPosBaseIdx = pu.mmvdMergeIdx / MMVD_MAX_REFINE_NUM;
+          PU::getInterMergeCandidates(pu, mrgCtx, 1, fPosBaseIdx + 1);
+#else
+          PU::getInterMergeCandidates(pu, mrgCtx, 255);
+#endif
+          PU::getInterMMVDMergeCandidates(pu, mrgCtx,
+            pu.mmvdMergeIdx
+          );
+        }
+        mrgCtx.setMmvdMergeCandiInfo(pu, pu.mmvdMergeIdx);
+
+        if (pu.interDir == 3 /* PRED_BI */ && PU::isBipredRestriction(pu))
+        {
+          pu.mv[REF_PIC_LIST_1] = Mv(0, 0);
+          pu.refIdx[REF_PIC_LIST_1] = -1;
+          pu.interDir = 1;
+        }
+
+        PU::spanMotionInfo(pu, mrgCtx);
+      }
+      else
+      {
+#endif
       {
         if( pu.cu->affine )
         {
@@ -461,14 +518,22 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
               PredictionUnit           tmpPU    = pu;
               static_cast<UnitArea&> ( tmpPU )  = cu;
               std::swap( tmpPS, cu.partSize );
+#if JVET_L0054_MMVD
+              PU::getInterMergeCandidates(tmpPU, mrgCtx, 0, pu.mergeIdx);
+#else
               PU::getInterMergeCandidates( tmpPU, mrgCtx, pu.mergeIdx );
+#endif
               std::swap( tmpPS, cu.partSize );
               mrgCtx.hasMergedCandList          = true;
             }
           }
           else
           {
+#if JVET_L0054_MMVD
+            PU::getInterMergeCandidates(pu, mrgCtx, 0, pu.mergeIdx);
+#else
             PU::getInterMergeCandidates( pu, mrgCtx, pu.mergeIdx );
+#endif
           }
 
           mrgCtx.setMergeInfo( pu, pu.mergeIdx );
@@ -486,6 +551,9 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
           PU::spanMotionInfo( pu, mrgCtx );
         }
       }
+#if JVET_L0054_MMVD
+      }
+#endif
     }
     else
     {
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 03d4197ea3e76b0331654aaa8e8fa39971d8dbb3..7636c2b34f332af8b0c695fa3854eb66ccde4002 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -666,6 +666,13 @@ void CABACWriter::cu_skip_flag( const CodingUnit& cu )
   m_BinEncoder.encodeBin( ( cu.skip ), Ctx::SkipFlag( ctxId ) );
 
   DTRACE( g_trace_ctx, D_SYNTAX, "cu_skip_flag() ctx=%d skip=%d\n", ctxId, cu.skip ? 1 : 0 );
+#if JVET_L0054_MMVD
+  if (cu.skip)
+  {
+    m_BinEncoder.encodeBin(cu.mmvdSkip, Ctx::MmvdFlag(0));
+    DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_cu_skip_flag() ctx=%d mmvd_skip=%d\n", 0, cu.mmvdSkip ? 1 : 0);
+  }
+#endif
 }
 
 
@@ -1114,6 +1121,13 @@ void CABACWriter::prediction_unit( const PredictionUnit& pu )
   if( pu.mergeFlag )
   {
     affine_flag  ( *pu.cu );
+#if JVET_L0054_MMVD
+    if (pu.mmvdMergeFlag)
+    {
+      mmvd_merge_idx(pu);
+    }
+    else
+#endif
     merge_idx    ( pu );
   }
   else
@@ -1180,7 +1194,12 @@ void CABACWriter::affine_flag( const CodingUnit& cu )
   }
 
   CHECK( !cu.cs->pcv->rectCUs && cu.lumaSize().width != cu.lumaSize().height, "CU width and height are not equal for QTBT off." );
-
+#if JVET_L0054_MMVD
+  if (cu.firstPU->mergeFlag && (cu.firstPU->mmvdMergeFlag || cu.mmvdSkip))
+  {
+    return;
+  }
+#endif
   unsigned ctxId = DeriveCtx::CtxAffineFlag( cu );
   m_BinEncoder.encodeBin( cu.affine, Ctx::AffineFlag( ctxId ) );
   DTRACE( g_trace_ctx, D_COMMON, " (%d) affine_flag() affine=%d\n", DTRACE_GET_COUNTER(g_trace_ctx, D_COMMON), cu.affine ? 1 : 0 );
@@ -1202,6 +1221,13 @@ void CABACWriter::merge_flag( const PredictionUnit& pu )
   m_BinEncoder.encodeBin( pu.mergeFlag, Ctx::MergeFlag() );
 
   DTRACE( g_trace_ctx, D_SYNTAX, "merge_flag() merge=%d pos=(%d,%d) size=%dx%d\n", pu.mergeFlag ? 1 : 0, pu.lumaPos().x, pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height );
+#if JVET_L0054_MMVD
+  if (pu.mergeFlag)
+  {
+    m_BinEncoder.encodeBin(pu.mmvdMergeFlag, Ctx::MmvdFlag(0));
+    DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_merge_flag() mmvd_merge=%d pos=(%d,%d) size=%dx%d\n", pu.mmvdMergeFlag ? 1 : 0, pu.lumaPos().x, pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height);
+  }
+#endif
 }
 
 void CABACWriter::imv_mode( const CodingUnit& cu )
@@ -1277,7 +1303,65 @@ void CABACWriter::merge_idx( const PredictionUnit& pu )
   }
   DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() merge_idx=%d\n", pu.mergeIdx );
 }
+#if JVET_L0054_MMVD
+void CABACWriter::mmvd_merge_idx(const PredictionUnit& pu)
+{
+  int var0, var1, var2;
+  int mvpIdx = pu.mmvdMergeIdx;
+  var0 = mvpIdx / MMVD_MAX_REFINE_NUM;
+  var1 = (mvpIdx - (var0 * MMVD_MAX_REFINE_NUM)) / 4;
+  var2 = mvpIdx - (var0 * MMVD_MAX_REFINE_NUM) - var1 * 4;
 
+  int numCandminus1_base = MMVD_BASE_MV_NUM - 1;
+  if (numCandminus1_base > 0)
+  {
+    if (var0 == 0)
+    {
+      m_BinEncoder.encodeBin(0, Ctx::MmvdMergeIdx());
+    }
+    else
+    {
+      m_BinEncoder.encodeBin(1, Ctx::MmvdMergeIdx());
+      for (unsigned idx = 1; idx < numCandminus1_base; idx++)
+      {
+        m_BinEncoder.encodeBinEP(var0 == idx ? 0 : 1);
+        if (var0 == idx)
+        {
+          break;
+        }
+      }
+    }
+  }
+  DTRACE(g_trace_ctx, D_SYNTAX, "base_mvp_idx() base_mvp_idx=%d\n", var0);
+
+  int numCandminus1_step = MMVD_REFINE_STEP - 1;
+  if (numCandminus1_step > 0)
+  {
+    if (var1 == 0)
+    {
+      m_BinEncoder.encodeBin(0, Ctx::MmvdStepMvpIdx());
+    }
+    else
+    {
+      m_BinEncoder.encodeBin(1, Ctx::MmvdStepMvpIdx());
+      for (unsigned idx = 1; idx < numCandminus1_step; idx++)
+      {
+        m_BinEncoder.encodeBinEP(var1 == idx ? 0 : 1);
+        if (var1 == idx)
+        {
+          break;
+        }
+      }
+    }
+  }
+  DTRACE(g_trace_ctx, D_SYNTAX, "MmvdStepMvpIdx() MmvdStepMvpIdx=%d\n", var1);
+
+  m_BinEncoder.encodeBinsEP(var2, 2);
+
+  DTRACE(g_trace_ctx, D_SYNTAX, "pos() pos=%d\n", var2);
+  DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_merge_idx() mmvd_merge_idx=%d\n", pu.mmvdMergeIdx);
+}
+#endif
 void CABACWriter::inter_pred_idc( const PredictionUnit& pu )
 {
   if( !pu.cs->slice->isInterB() )
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index b17bfadeef0a8c6034921de77b5838e451e51257..b597aa474d329e7271ebff05054f4df528db5fb5 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -110,6 +110,9 @@ public:
   void        merge_flag                ( const PredictionUnit&         pu );
   void        affine_flag               ( const CodingUnit&             cu );
   void        merge_idx                 ( const PredictionUnit&         pu );
+#if JVET_L0054_MMVD
+  void        mmvd_merge_idx(const PredictionUnit&         pu);
+#endif
   void        imv_mode                  ( const CodingUnit&             cu );
   void        inter_pred_idc            ( const PredictionUnit&         pu );
   void        ref_idx                   ( const PredictionUnit&         pu,       RefPicList        eRefList );
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 60b08917056def793fde1fd21b93894c8a99b258..8295455e5237eb70e7d7e8a08ff4076c9f2c3b3a 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -151,7 +151,11 @@ void EncCu::create( EncCfg* encCfg )
   m_modeCtrl->create( *encCfg );
 
 #endif
+#if JVET_L0054_MMVD
+  for (unsigned ui = 0; ui < MMVD_MRG_MAX_RD_BUF_NUM; ui++)
+#else
   for( unsigned ui = 0; ui < MRG_MAX_NUM_CANDS; ui++ )
+#endif
   {
     m_acMergeBuffer[ui].create( chromaFormat, Area( 0, 0, uiMaxWidth, uiMaxHeight ) );
   }
@@ -606,6 +610,10 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
     else if( currTestMode.type == ETM_MERGE_SKIP )
     {
       xCheckRDCostMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode );
+#if JVET_L0054_MMVD
+      CodingUnit* cu = bestCS->getCU(partitioner.chType);
+      cu->mmvdSkip = cu->skip == false ? false : cu->mmvdSkip;
+#endif
     }
     else if( currTestMode.type == ETM_INTRA )
     {
@@ -1131,6 +1139,9 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC
     cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
 #endif
     cu.skip             = false;
+#if JVET_L0054_MMVD
+    cu.mmvdSkip = false;
+#endif
     cu.partSize         = encTestMode.partSize;
     cu.predMode         = MODE_INTRA;
     cu.transQuantBypass = encTestMode.lossless;
@@ -1244,6 +1255,9 @@ void EncCu::xCheckIntraPCM(CodingStructure *&tempCS, CodingStructure *&bestCS, P
   cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
 #endif
   cu.skip             = false;
+#if JVET_L0054_MMVD
+  cu.mmvdSkip = false;
+#endif
   cu.partSize         = SIZE_2Nx2N;
   cu.predMode         = MODE_INTRA;
   cu.transQuantBypass = encTestMode.lossless;
@@ -1413,8 +1427,14 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     PredictionUnit pu( tempCS->area );
     pu.cu = &cu;
     pu.cs = tempCS;
-
-    PU::getInterMergeCandidates(pu, mergeCtx);
+    PU::getInterMergeCandidates(pu, mergeCtx
+#if JVET_L0054_MMVD
+      , 0
+#endif
+    );
+#if JVET_L0054_MMVD
+    PU::getInterMMVDMergeCandidates(pu, mergeCtx);
+#endif
 #if JVET_L0104_NO_4x4BI_INTER_CU
     if (PU::isBipredRestriction(pu))
     {
@@ -1432,8 +1452,43 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     }
 #endif
   }
+#if JVET_L0054_MMVD
+  bool candHasNoResidual[MRG_MAX_NUM_CANDS + MMVD_ADD_NUM];
+  for (uint32_t ui = 0; ui < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; ui++)
+  {
+    candHasNoResidual[ui] = false;
+  }
 
+  bool                                        bestIsSkip = false;
+  bool                                        bestIsMMVDSkip = true;
+  PelUnitBuf                                  acMergeRealBuffer[MMVD_MRG_MAX_RD_BUF_NUM];
+  PelUnitBuf *                                acMergeTempBuffer[MMVD_MRG_MAX_RD_NUM];
+  PelUnitBuf *                                singleMergeTempBuffer;
+  int                                         insertPos;
+  unsigned                                    uiNumMrgSATDCand = mergeCtx.numValidMergeCand + MMVD_ADD_NUM;
+
+  static_vector<unsigned, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM>  RdModeList;
+  bool                                        mrgTempBufSet = false;
+
+  for (unsigned i = 0; i < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; i++)
+  {
+    RdModeList.push_back(i);
+  }
 
+  const UnitArea localUnitArea(tempCS->area.chromaFormat, Area(0, 0, tempCS->area.Y().width, tempCS->area.Y().height));
+  for (unsigned i = 0; i < MMVD_MRG_MAX_RD_BUF_NUM; i++)
+  {
+    acMergeRealBuffer[i] = m_acMergeBuffer[i].getBuf(localUnitArea);
+    if (i < MMVD_MRG_MAX_RD_NUM)
+    {
+      acMergeTempBuffer[i] = acMergeRealBuffer + i;
+    }
+    else
+    {
+      singleMergeTempBuffer = acMergeRealBuffer + i;
+    }
+  }
+#else
   bool candHasNoResidual[MRG_MAX_NUM_CANDS];
   for (uint32_t ui = 0; ui < mergeCtx.numValidMergeCand; ui++)
   {
@@ -1454,7 +1509,7 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
   {
     RdModeList.push_back( i );
   }
-
+#endif
   if( m_pcEncCfg->getUseFastMerge() )
   {
     uiNumMrgSATDCand = NUM_MRG_SATD_CAND;
@@ -1463,10 +1518,15 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
     {
       bestIsSkip = blkCache->isSkip( tempCS->area );
+#if JVET_L0054_MMVD
+      bestIsMMVDSkip = blkCache->isMMVDSkip(tempCS->area);
+#endif
     }
-
+#if JVET_L0054_MMVD
+    static_vector<double, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM> candCostList;
+#else
     static_vector<double, MRG_MAX_NUM_CANDS> candCostList;
-
+#endif
     // 1. Pass: get SATD-cost for selected candidates and reduce their count
     if( !bestIsSkip )
     {
@@ -1482,6 +1542,9 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
 #endif
       cu.skip             = false;
+#if JVET_L0054_MMVD
+      cu.mmvdSkip = false;
+#endif
       cu.partSize         = SIZE_2Nx2N;
     //cu.affine
       cu.predMode         = MODE_INTER;
@@ -1500,16 +1563,20 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       const UnitArea localUnitArea( tempCS->area.chromaFormat, Area( 0, 0, tempCS->area.Y().width, tempCS->area.Y().height) );
       for( uint32_t uiMergeCand = 0; uiMergeCand < mergeCtx.numValidMergeCand; uiMergeCand++ )
       {
+#if !JVET_L0054_MMVD
         acMergeBuffer[uiMergeCand] = m_acMergeBuffer[uiMergeCand].getBuf( localUnitArea );
-
+#endif
         mergeCtx.setMergeInfo( pu, uiMergeCand );
 
         PU::spanMotionInfo( pu, mergeCtx );
-
+#if JVET_L0054_MMVD
+        distParam.cur = singleMergeTempBuffer->Y();
+        m_pcInterSearch->motionCompensation(pu, *singleMergeTempBuffer);
+#else
         distParam.cur = acMergeBuffer[uiMergeCand].Y();
 
         m_pcInterSearch->motionCompensation( pu,  acMergeBuffer[uiMergeCand] );
-        
+#endif
         if( mergeCtx.interDirNeighbours[uiMergeCand] == 3 && mergeCtx.mrgTypeNeighbours[uiMergeCand] == MRG_TYPE_DEFAULT_N )
         {
           mergeCtx.mvFieldNeighbours[2*uiMergeCand].mv   = pu.mv[0];
@@ -1525,11 +1592,121 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         {
           uiBitsCand--;
         }
+#if JVET_L0054_MMVD
+        uiBitsCand++; // for mmvd_flag
+#endif
         double cost     = (double)uiSad + (double)uiBitsCand * sqrtLambdaForFirstPass;
-
+#if JVET_L0054_MMVD
+        insertPos = -1;
+        updateCandList(uiMergeCand, cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos);
+        if (insertPos != -1)
+        {
+          if (insertPos == RdModeList.size() - 1)
+          {
+            swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
+          }
+          else
+          {
+            for (uint32_t i = uint32_t(RdModeList.size()) - 1; i > insertPos; i--)
+            {
+              swap(acMergeTempBuffer[i - 1], acMergeTempBuffer[i]);
+            }
+            swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
+          }
+        }
+#else
         updateCandList( uiMergeCand, cost, RdModeList, candCostList, uiNumMrgSATDCand );
+#endif
         CHECK( std::min( uiMergeCand + 1, uiNumMrgSATDCand ) != RdModeList.size(), "" );
       }
+#if JVET_L0054_MMVD
+      cu.mmvdSkip = true;
+      int tempNum = 0;
+      tempNum = MMVD_ADD_NUM;
+      bool allowDirection[4] = { true, true, true, true };
+      for (uint32_t mergeCand = mergeCtx.numValidMergeCand; mergeCand < mergeCtx.numValidMergeCand + tempNum; mergeCand++)
+      {
+        const int mmvdMergeCand = mergeCand - mergeCtx.numValidMergeCand;
+        int bitsBaseIdx = 0;
+        int bitsRefineStep = 0;
+        int bitsDirection = 2;
+        int bitsCand = 0;
+        int baseIdx;
+        int refineStep;
+        int direction;
+        baseIdx = mmvdMergeCand / MMVD_MAX_REFINE_NUM;
+        refineStep = (mmvdMergeCand - (baseIdx * MMVD_MAX_REFINE_NUM)) / 4;
+        direction = (mmvdMergeCand - baseIdx * MMVD_MAX_REFINE_NUM - refineStep * 4) % 4;
+        if (refineStep == 0)
+        {
+          allowDirection[direction] = true;
+        }
+        if (allowDirection[direction] == false)
+        {
+          continue;
+        }
+        bitsBaseIdx = baseIdx + 1;
+        if (baseIdx == MMVD_BASE_MV_NUM - 1)
+        {
+          bitsBaseIdx--;
+        }
+
+        bitsRefineStep = refineStep + 1;
+        if (refineStep == MMVD_REFINE_STEP - 1)
+        {
+          bitsRefineStep--;
+        }
+
+        bitsCand = bitsBaseIdx + bitsRefineStep + bitsDirection;
+        bitsCand++; // for mmvd_flag
+
+#if !JVET_L0054_MMVD
+        acMergeBuffer[mergeCand] = m_acMergeBuffer[mergeCand].getBuf(localUnitArea);
+#endif
+        mergeCtx.setMmvdMergeCandiInfo(pu, mmvdMergeCand);
+
+        PU::spanMotionInfo(pu, mergeCtx);
+#if JVET_L0054_MMVD
+        distParam.cur = singleMergeTempBuffer->Y();
+        m_pcInterSearch->motionCompensation(pu, *singleMergeTempBuffer);
+#else
+        distParam.cur = acMergeBuffer[mergeCand].Y();
+        m_pcInterSearch->motionCompensation(pu, acMergeBuffer[mergeCand]);
+#endif
+
+        Distortion uiSad = distParam.distFunc(distParam);
+
+
+#if !JVET_L0054_MMVD
+        uint32_t bitsCand = mergeCand + 1;
+        if (mergeCand == tempCS->slice->getMaxNumMergeCand() - 1)
+        {
+          bitsCand--;
+        }
+#endif
+        double cost = (double)uiSad + (double)bitsCand * sqrtLambdaForFirstPass;
+#if JVET_L0054_MMVD
+        allowDirection[direction] = cost >  1.3 * candCostList[0] ? 0 : 1;
+#endif
+#if JVET_L0054_MMVD
+        insertPos = -1;
+        updateCandList(mergeCand, cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos);
+        if (insertPos != -1)
+        {
+          for (int i = int(RdModeList.size()) - 1; i > insertPos; i--)
+          {
+            swap(acMergeTempBuffer[i - 1], acMergeTempBuffer[i]);
+          }
+          swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
+        }
+#else
+        updateCandList(mergeCand, cost, RdModeList, candCostList, uiNumMrgSATDCand);
+#endif
+#if !JVET_L0054_MMVD
+        CHECK(std::min(mergeCand + 1, uiNumMrgSATDCand) != RdModeList.size(), "");
+#endif
+      }
+#endif
       // Try to limit number of candidates using SATD-costs
       for( uint32_t i = 1; i < uiNumMrgSATDCand; i++ )
       {
@@ -1544,7 +1721,18 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     }
     else
     {
+#if JVET_L0054_MMVD
+      if (bestIsMMVDSkip)
+      {
+        uiNumMrgSATDCand = mergeCtx.numValidMergeCand + MMVD_ADD_NUM;
+      }
+      else
+      {
+        uiNumMrgSATDCand = mergeCtx.numValidMergeCand;
+      }
+#else
       uiNumMrgSATDCand = mergeCtx.numValidMergeCand;
+#endif
     }
   }
 
@@ -1556,7 +1744,11 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     for( uint32_t uiMrgHADIdx = 0; uiMrgHADIdx < uiNumMrgSATDCand; uiMrgHADIdx++ )
     {
       uint32_t uiMergeCand = RdModeList[uiMrgHADIdx];
+#if JVET_L0054_MMVD
+      if (((uiNoResidualPass != 0) && candHasNoResidual[uiMrgHADIdx])
+#else
       if( ( (uiNoResidualPass != 0) && candHasNoResidual[uiMergeCand] )
+#endif
        || ( (uiNoResidualPass == 0) && bestIsSkip ) )
       {
         continue;
@@ -1571,6 +1763,9 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
 #endif
       cu.skip             = false;
+#if JVET_L0054_MMVD
+      cu.mmvdSkip = false;
+#endif
       cu.partSize         = SIZE_2Nx2N;
     //cu.affine
       cu.predMode         = MODE_INTER;
@@ -1579,8 +1774,20 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.chromaQpAdj      = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
       cu.qp               = encTestMode.qp;
       PredictionUnit &pu  = tempCS->addPU( cu, partitioner.chType );
-
+#if JVET_L0054_MMVD
+      if (uiMergeCand >= mergeCtx.numValidMergeCand)
+      {
+        cu.mmvdSkip = true;
+        mergeCtx.setMmvdMergeCandiInfo(pu, uiMergeCand - mergeCtx.numValidMergeCand);
+      }
+      else
+      {
+        cu.mmvdSkip = false;
+        mergeCtx.setMergeInfo(pu, uiMergeCand);
+      }
+#else
       mergeCtx.setMergeInfo( pu, uiMergeCand );
+#endif
       PU::spanMotionInfo( pu, mergeCtx );
 
       if( mrgTempBufSet )
@@ -1588,18 +1795,28 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
 #if DMVR_JVET_LOW_LATENCY_K0217
         pu.mvd[0] = refinedMvdL0[uiMergeCand];
 #endif
+#if JVET_L0054_MMVD
+        tempCS->getPredBuf().copyFrom(*acMergeTempBuffer[uiMrgHADIdx]);
+#else
         tempCS->getPredBuf().copyFrom( acMergeBuffer[ uiMergeCand ]);
+#endif
       }
       else
       {
         m_pcInterSearch->motionCompensation( pu );
         
       }
-
+#if JVET_L0054_MMVD
+      xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass
+        , NULL
+        , 1
+        , uiNoResidualPass == 0 ? &candHasNoResidual[uiMrgHADIdx] : NULL);
+#else
       xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass
         , NULL
         , 1
         , uiNoResidualPass == 0 ? &candHasNoResidual[uiMergeCand] : NULL );
+#endif
 
       if( m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip )
       {
@@ -1672,6 +1889,9 @@ void EncCu::xCheckRDCostAffineMerge2Nx2N( CodingStructure *&tempCS, CodingStruct
   cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
 #endif
   cu.skip             = false;
+#if JVET_L0054_MMVD
+  cu.mmvdSkip = false;
+#endif
   cu.partSize         = encTestMode.partSize;
   cu.affine           = true;
   cu.predMode         = MODE_INTER;
@@ -1781,6 +2001,9 @@ void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestC
   cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
 #endif
   cu.skip             = false;
+#if JVET_L0054_MMVD
+  cu.mmvdSkip = false;
+#endif
   cu.partSize         = encTestMode.partSize;
 //cu.affine
   cu.predMode         = MODE_INTER;
@@ -1948,6 +2171,9 @@ bool EncCu::xCheckRDCostInterIMV( CodingStructure *&tempCS, CodingStructure *&be
     cu.tileIdx          = tempCS->picture->tileMap->getTileIdxMap( tempCS->area.lumaPos() );
 #endif
     cu.skip             = false;
+#if JVET_L0054_MMVD
+    cu.mmvdSkip = false;
+#endif
     cu.partSize         = encTestMode.partSize;
   //cu.affine
     cu.predMode         = MODE_INTER;
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index e5df1314e452666f2b3512a7fda92c328ff00dac..7e0512e3c3d5d81c84c87e23507e557896566afd 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -107,9 +107,11 @@ private:
   RateCtrl*             m_pcRateCtrl;
   CodingStructure    ***m_pImvTempCS;
   EncModeCtrl          *m_modeCtrl;
-
+#if JVET_L0054_MMVD
+  PelStorage            m_acMergeBuffer[MMVD_MRG_MAX_RD_BUF_NUM];
+#else
   PelStorage            m_acMergeBuffer[MRG_MAX_NUM_CANDS];
-
+#endif
   MotionInfo            m_SubPuMiBuf      [( MAX_CU_SIZE * MAX_CU_SIZE ) >> ( MIN_CU_LOG2 << 1 )];
   unsigned int          m_subMergeBlkSize[10];
   unsigned int          m_subMergeBlkNum[10];
diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp
index 115f532997460397c273eed0e5998111110ea8c7..784903a0135979e42ac875ca516869e70f05bf61 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.cpp
+++ b/source/Lib/EncoderLib/EncModeCtrl.cpp
@@ -473,6 +473,16 @@ bool CacheBlkInfoCtrl::isSkip( const UnitArea& area )
   return m_codedCUInfo[idx1][idx2][idx3][idx4]->isSkip;
 }
 
+#if JVET_L0054_MMVD
+bool CacheBlkInfoCtrl::isMMVDSkip(const UnitArea& area)
+{
+  unsigned idx1, idx2, idx3, idx4;
+  getAreaIdx(area.Y(), *m_slice_chblk->getPPS()->pcv, idx1, idx2, idx3, idx4);
+
+  return m_codedCUInfo[idx1][idx2][idx3][idx4]->isMMVDSkip;
+}
+#endif
+
 void CacheBlkInfoCtrl::setMv( const UnitArea& area, const RefPicList refPicList, const int iRefIdx, const Mv& rMv )
 {
   if( iRefIdx >= MAX_STORED_CU_INFO_REFS ) return;
@@ -1509,6 +1519,9 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt
           relatedCU.isInter   = true;
 #if HM_CODED_CU_INFO
           relatedCU.isSkip   |= bestCU->skip;
+#if JVET_L0054_MMVD
+          relatedCU.isMMVDSkip |= bestCU->mmvdSkip;
+#endif
 #else
           relatedCU.isSkip    = bestCU->skip;
 #endif
diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h
index 987c6a6750dc5ff2fb66ab023c83c529c5324bdd..f56fceb06b1435025bced67fcf32150c1a4c7253 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.h
+++ b/source/Lib/EncoderLib/EncModeCtrl.h
@@ -317,7 +317,9 @@ struct CodedCUInfo
   bool isInter;
   bool isIntra;
   bool isSkip;
-
+#if JVET_L0054_MMVD
+  bool isMMVDSkip;
+#endif
   bool validMv[NUM_REF_PIC_LIST_01][MAX_STORED_CU_INFO_REFS];
   Mv   saveMv [NUM_REF_PIC_LIST_01][MAX_STORED_CU_INFO_REFS];
 
@@ -368,7 +370,9 @@ public:
   virtual ~CacheBlkInfoCtrl() {}
 
   bool isSkip ( const UnitArea& area );
-
+#if JVET_L0054_MMVD
+  bool isMMVDSkip(const UnitArea& area);
+#endif
   bool getMv  ( const UnitArea& area, const RefPicList refPicList, const int iRefIdx,       Mv& rMv ) const;
   void setMv  ( const UnitArea& area, const RefPicList refPicList, const int iRefIdx, const Mv& rMv );
 
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index 8e01532c7e6012e760088d6b3fd92d1d1924d7c9..c3267fac82b983c0178909aeadf0aa5f69dfa548 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -692,16 +692,54 @@ void InterSearch::xMergeEstimation( PredictionUnit& pu, PelUnitBuf& origBuf, int
 
       pu.UnitArea::operator=( *pu.cu );
       pu.cu->partSize = SIZE_2Nx2N;
-
+#if JVET_L0054_MMVD
+      if(pu.mmvdMergeFlag)
+      {
+      PU::getInterMergeCandidates( pu, mergeCtx 
+#if JVET_L0054_MMVD
+        , 0
+#endif
+      );
+        PU::getInterMMVDMergeCandidates(pu, mergeCtx);
+      }
+      else
+      {
+        PU::getInterMergeCandidates(pu, mergeCtx
+#if JVET_L0054_MMVD
+          , 0
+#endif
+        );
+      }
+#else
       PU::getInterMergeCandidates( pu, mergeCtx );
-
+#endif
       pu.UnitArea::operator=( unitArea );
       pu.cu->partSize = partSize;
     }
   }
   else
   {
+#if JVET_L0054_MMVD
+    if(pu.mmvdMergeFlag)
+    {
+    PU::getInterMergeCandidates( pu, mergeCtx 
+#if JVET_L0054_MMVD
+      , 0
+#endif
+    );
+      PU::getInterMMVDMergeCandidates(pu, mergeCtx);
+    }
+    else
+    {
+      PU::getInterMergeCandidates(pu, mergeCtx
+#if JVET_L0054_MMVD
+        , 0
+#endif
+      );
+    }
+#else
     PU::getInterMergeCandidates( pu, mergeCtx );
+#endif
   }
 
   PU::restrictBiPredMergeCands( pu, mergeCtx );
@@ -4565,6 +4603,13 @@ void InterSearch::encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &pa
 
     m_CABACEstimator->cu_skip_flag  ( cu );
     m_CABACEstimator->affine_flag( cu );
+#if JVET_L0054_MMVD
+    if (cu.mmvdSkip)
+    {
+      m_CABACEstimator->mmvd_merge_idx(pu);
+    }
+    else
+#endif
     m_CABACEstimator->merge_idx     ( pu );
 
 
@@ -4689,6 +4734,13 @@ uint64_t InterSearch::xGetSymbolFracBitsInter(CodingStructure &cs, Partitioner &
 
     m_CABACEstimator->cu_skip_flag  ( cu );
     m_CABACEstimator->affine_flag   ( cu );
+#if JVET_L0054_MMVD
+    if (cu.mmvdSkip)
+    {
+      m_CABACEstimator->mmvd_merge_idx(*cu.firstPU);
+    }
+    else
+#endif
     m_CABACEstimator->merge_idx     ( *cu.firstPU );
     fracBits   += m_CABACEstimator->getEstFracBits();
   }