--- trunk/xvidcore/src/motion/motion_est.c 2002/06/30 10:46:29 252 +++ trunk/xvidcore/src/motion/motion_est.c 2002/07/10 20:11:05 289 @@ -1,5 +1,35 @@ /************************************************************************** * + * XVID MPEG-4 VIDEO CODEC + * motion estimation + * + * This program is an implementation of a part of one or more MPEG-4 + * Video tools as specified in ISO/IEC 14496-2 standard. Those intending + * to use this software module in hardware or software products are + * advised that its use may infringe existing patents or copyrights, and + * any such use would be at such party's own risk. The original + * developer of this software module and his/her company, and subsequent + * editors and their companies, will have no liability for use of this + * software or modifications or derivatives thereof. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *************************************************************************/ + +/************************************************************************** + * * Modifications: * * 01.05.2002 updated MotionEstimationBVOP @@ -47,156 +77,7 @@ #include "motion.h" #include "sad.h" -// very large value -#define MV_MAX_ERROR (4096 * 256) - -// stop search if sdelta < THRESHOLD -#define MV16_THRESHOLD 192 -#define MV8_THRESHOLD 56 - -#define NEIGH_MOVE_THRESH 0 -// how much a block's MV must differ from his neighbour -// to be search for INTER4V. The more, the faster... - -/* sad16(0,0) bias; mpeg4 spec suggests nb/2+1 */ -/* nb = vop pixels * 2^(bpp-8) */ -#define MV16_00_BIAS (128+1) -#define MV8_00_BIAS (0) - -/* INTER bias for INTER/INTRA decision; mpeg4 spec suggests 2*nb */ -#define MV16_INTER_BIAS 512 - -/* Parameters which control inter/inter4v decision */ -#define IMV16X16 5 - -/* vector map (vlc delta size) smoother parameters */ -#define NEIGH_TEND_16X16 2 -#define NEIGH_TEND_8X8 2 - -// fast ((A)/2)*2 -#define EVEN(A) (((A)<0?(A)+1:(A)) & ~1) - -#define MVzero(A) ( ((A).x)==(0) && ((A).y)==(0) ) -#define MVequal(A,B) ( ((A).x)==((B).x) && ((A).y)==((B).y) ) - -int32_t PMVfastSearch16(const uint8_t * const pRef, - const uint8_t * const pRefH, - const uint8_t * const pRefV, - const uint8_t * const pRefHV, - const IMAGE * const pCur, - const int x, - const int y, - const uint32_t MotionFlags, - const uint32_t iQuant, - const uint32_t iFcode, - const MBParam * const pParam, - const MACROBLOCK * const pMBs, - const MACROBLOCK * const prevMBs, - VECTOR * const currMV, - VECTOR * const currPMV); - -int32_t EPZSSearch16(const uint8_t * const pRef, - const uint8_t * const pRefH, - const uint8_t * const pRefV, - const uint8_t * const pRefHV, - const IMAGE * const pCur, - const int x, - const int y, - const uint32_t MotionFlags, - const uint32_t iQuant, - const uint32_t iFcode, - const MBParam * const pParam, - const MACROBLOCK * const pMBs, - const MACROBLOCK * const prevMBs, - VECTOR * const currMV, - VECTOR * const currPMV); - -int32_t PMVfastSearch8(const uint8_t * const pRef, - const uint8_t * const pRefH, - const uint8_t * const pRefV, - const uint8_t * const pRefHV, - const IMAGE * const pCur, - const int x, - const int y, - const int start_x, - const int start_y, - const uint32_t MotionFlags, - const uint32_t iQuant, - const uint32_t iFcode, - const MBParam * const pParam, - const MACROBLOCK * const pMBs, - const MACROBLOCK * const prevMBs, - VECTOR * const currMV, - VECTOR * const currPMV); - -int32_t EPZSSearch8(const uint8_t * const pRef, - const uint8_t * const pRefH, - const uint8_t * const pRefV, - const uint8_t * const pRefHV, - const IMAGE * const pCur, - const int x, - const int y, - const int start_x, - const int start_y, - const uint32_t MotionFlags, - const uint32_t iQuant, - const uint32_t iFcode, - const MBParam * const pParam, - const MACROBLOCK * const pMBs, - const MACROBLOCK * const prevMBs, - VECTOR * const currMV, - VECTOR * const currPMV); - - -typedef int32_t(MainSearch16Func) (const uint8_t * const pRef, - const uint8_t * const pRefH, - const uint8_t * const pRefV, - const uint8_t * const pRefHV, - const uint8_t * const cur, - const int x, - const int y, - int32_t startx, - int32_t starty, - int32_t iMinSAD, - VECTOR * const currMV, - const VECTOR * const pmv, - const int32_t min_dx, - const int32_t max_dx, - const int32_t min_dy, - const int32_t max_dy, - const int32_t iEdgedWidth, - const int32_t iDiamondSize, - const int32_t iFcode, - const int32_t iQuant, - int iFound); - -typedef MainSearch16Func *MainSearch16FuncPtr; - - -typedef int32_t(MainSearch8Func) (const uint8_t * const pRef, - const uint8_t * const pRefH, - const uint8_t * const pRefV, - const uint8_t * const pRefHV, - const uint8_t * const cur, - const int x, - const int y, - int32_t startx, - int32_t starty, - int32_t iMinSAD, - VECTOR * const currMV, - const VECTOR * const pmv, - const int32_t min_dx, - const int32_t max_dx, - const int32_t min_dy, - const int32_t max_dy, - const int32_t iEdgedWidth, - const int32_t iDiamondSize, - const int32_t iFcode, - const int32_t iQuant, - int iFound); - -typedef MainSearch8Func *MainSearch8FuncPtr; static int32_t lambda_vec16[32] = /* rounded values for lambda param for weight of motion bits as in modified H.26L */ { 0, (int) (1.00235 + 0.5), (int) (1.15582 + 0.5), (int) (1.31976 + 0.5), @@ -273,22 +154,7 @@ return NEIGH_TEND_8X8 * lambda_vec8[iQuant] * (mv_bits(dx, iFcode) + mv_bits(dy, iFcode)); } - - - - - -#ifndef SEARCH16 -#define SEARCH16 PMVfastSearch16 -//#define SEARCH16 FullSearch16 -//#define SEARCH16 EPZSSearch16 -#endif - -#ifndef SEARCH8 -#define SEARCH8 PMVfastSearch8 -//#define SEARCH8 EPZSSearch8 -#endif - + bool MotionEstimation(MBParam * const pParam, FRAMEINFO * const current, @@ -314,8 +180,9 @@ if (sadInit) (*sadInit) (); - for (y = 0; y < iHcount; y++) - for (x = 0; x < iWcount; x++) { + for (y = 0; y < iHcount; y++) { + for (x = 0; x < iWcount; x ++) { + MACROBLOCK *const pMB = &pMBs[x + y * iWcount]; pMB->sad16 = @@ -409,8 +276,9 @@ pMB->mvs[0] = pMB->mvs[1] = pMB->mvs[2] = pMB->mvs[3] = pMB->mv16; pMB->sad8[0] = pMB->sad8[1] = pMB->sad8[2] = pMB->sad8[3] = pMB->sad16; + } + } - } return 0; } @@ -528,9 +396,10 @@ const int32_t iEdgedWidth = pParam->edged_width; const uint8_t * cur = pCur->y + x*16 + y*16*iEdgedWidth; int32_t iSAD; - int32_t pred_x,pred_y; + VECTOR pred; + - get_pmv(pMBs, x, y, pParam->mb_width, 0, &pred_x, &pred_y); + pred = get_pmv2(pMBs, pParam->mb_width, 0, x, y, 0); iSAD = sad16( cur, get_ref(pRef, pRefH, pRefV, pRefHV, x, y, 16, 0,0, iEdgedWidth), @@ -540,8 +409,8 @@ currMV->x = 0; currMV->y = 0; - currPMV->x = -pred_x; - currPMV->y = -pred_y; + currPMV->x = -pred.x; + currPMV->y = -pred.y; return iSAD; @@ -1259,10 +1128,9 @@ MainSearch16FuncPtr MainSearchPtr; -// const MACROBLOCK * const pMB = pMBs + x + y * iWcount; const MACROBLOCK *const prevMB = prevMBs + x + y * iWcount; - static int32_t threshA, threshB; + int32_t threshA, threshB; int32_t bPredEq; int32_t iMinSAD, iSAD; @@ -1280,12 +1148,17 @@ } /* because we might use something like IF (dx>max_dx) THEN dx=max_dx; */ - bPredEq = get_pmvdata(pMBs, x, y, iWcount, 0, pmv, psad); + //bPredEq = get_pmvdata(pMBs, x, y, iWcount, 0, pmv, psad); + bPredEq = get_pmvdata2(pMBs, iWcount, 0, x, y, 0, pmv, psad); +/* fprintf(stderr,"pmv: %d %d / %d --- %d %d %d %d %d %d - %d %d %d\n", + pmv[0].x,pmv[0].y,psad[0], + pmv[1].x,pmv[1].y,pmv[2].x,pmv[2].y,pmv[3].x,pmv[3].y, + psad[1],psad[2],psad[3]); +*/ if ((x == 0) && (y == 0)) { threshA = 512; threshB = 1024; - } else { threshA = psad[0]; threshB = threshA + 256; @@ -1335,7 +1208,7 @@ if ((iMinSAD < 256) || ((MVequal(*currMV, prevMB->mvs[0])) && - ((uint32_t) iMinSAD < prevMB->sad16))) { + ((int32_t) iMinSAD < prevMB->sad16))) { if (iMinSAD < 2 * iQuant) // high chances for SKIP-mode { if (!MVzero(*currMV)) { @@ -1439,7 +1312,7 @@ if ((iMinSAD <= threshA) || (MVequal(*currMV, prevMB->mvs[0]) && - ((uint32_t) iMinSAD < prevMB->sad16))) { + ((int32_t) iMinSAD < prevMB->sad16))) { if (MotionFlags & PMV_QUICKSTOP16) goto PMVfast16_Terminate_without_Refine; if (MotionFlags & PMV_EARLYSTOP16) @@ -1466,6 +1339,9 @@ backupMV = *currMV; /* save best prediction, actually only for EXTSEARCH */ + +// fprintf(stderr,"Entering Diamond %d %d (%d):\n",x,y,iMinSAD); + /* default: use best prediction as starting point for one call of PMVfast_MainSearch */ iSAD = (*MainSearchPtr) (pRef, pRefH, pRefV, pRefHV, cur, x, y, currMV->x, @@ -1519,6 +1395,8 @@ iMinSAD, pmv, min_dx, max_dx, min_dy, max_dy, iFcode, iQuant, iEdgedWidth); +/*fprintf(stderr,"Chosen for %d %d: %d %d - %d %d\n",x,y,currMV->x,currMV->y,pmv[0].x,pmv[0].y); +*/ PMVfast16_Terminate_without_Refine: currPMV->x = currMV->x - pmv[0].x; currPMV->y = currMV->y - pmv[0].y; @@ -1674,7 +1552,7 @@ // const MACROBLOCK * const pMB = pMBs + (x>>1) + (y>>1) * iWcount; const MACROBLOCK *const prevMB = prevMBs + (x >> 1) + (y >> 1) * iWcount; - static int32_t threshA, threshB; + int32_t threshA, threshB; int32_t iFound, bPredEq; int32_t iMinSAD, iSAD; @@ -1698,8 +1576,8 @@ } /* because we might use IF (dx>max_dx) THEN dx=max_dx; */ - bPredEq = - get_pmvdata(pMBs, (x >> 1), (y >> 1), iWcount, iSubBlock, pmv, psad); + //bPredEq = get_pmvdata(pMBs, (x >> 1), (y >> 1), iWcount, iSubBlock, pmv, psad); + bPredEq = get_pmvdata2(pMBs, iWcount, 0, (x >> 1), (y >> 1), iSubBlock, pmv, psad); if ((x == 0) && (y == 0)) { threshA = 512 / 4; @@ -1749,7 +1627,7 @@ (uint8_t) iFcode, iQuant); if ((iMinSAD < 256 / 4) || ((MVequal(*currMV, prevMB->mvs[iSubBlock])) - && ((uint32_t) iMinSAD < + && ((int32_t) iMinSAD < prevMB->sad8[iSubBlock]))) { if (MotionFlags & PMV_QUICKSTOP16) goto PMVfast8_Terminate_without_Refine; @@ -1804,7 +1682,7 @@ if ((iMinSAD <= threshA) || (MVequal(*currMV, prevMB->mvs[iSubBlock]) && - ((uint32_t) iMinSAD < prevMB->sad8[iSubBlock]))) { + ((int32_t) iMinSAD < prevMB->sad8[iSubBlock]))) { if (MotionFlags & PMV_QUICKSTOP16) goto PMVfast8_Terminate_without_Refine; if (MotionFlags & PMV_EARLYSTOP16) @@ -1863,7 +1741,7 @@ if ((iMinSAD <= threshA) || (MVequal(*currMV, prevMB->mvs[iSubBlock]) && - ((uint32_t) iMinSAD < prevMB->sad8[iSubBlock]))) { + ((int32_t) iMinSAD < prevMB->sad8[iSubBlock]))) { if (MotionFlags & PMV_QUICKSTOP16) goto PMVfast8_Terminate_without_Refine; if (MotionFlags & PMV_EARLYSTOP16) @@ -1986,7 +1864,7 @@ const MACROBLOCK *const prevMB = prevMBs + x + y * iWcount; MACROBLOCK *oldMB = NULL; - static int32_t thresh2; + int32_t thresh2; int32_t bPredEq; int32_t iMinSAD, iSAD = 9999; @@ -2009,7 +1887,8 @@ max_dy = EVEN(max_dy); } /* because we might use something like IF (dx>max_dx) THEN dx=max_dx; */ - bPredEq = get_pmvdata(pMBs, x, y, iWcount, 0, pmv, psad); + //bPredEq = get_pmvdata(pMBs, x, y, iWcount, 0, pmv, psad); + bPredEq = get_pmvdata2(pMBs, iWcount, 0, x, y, 0, pmv, psad); /* Step 4: Calculate SAD around the Median prediction. MinSAD=SAD @@ -2048,7 +1927,7 @@ // thresh1 is fixed to 256 if ((iMinSAD < 256) || ((MVequal(*currMV, prevMB->mvs[0])) && - ((uint32_t) iMinSAD < prevMB->sad16))) { + ((int32_t) iMinSAD < prevMB->sad16))) { if (MotionFlags & PMV_QUICKSTOP16) goto EPZS16_Terminate_without_Refine; if (MotionFlags & PMV_EARLYSTOP16) @@ -2108,7 +1987,7 @@ if ((iMinSAD <= thresh2) || (MVequal(*currMV, prevMB->mvs[0]) && - ((uint32_t) iMinSAD <= prevMB->sad16))) { + ((int32_t) iMinSAD <= prevMB->sad16))) { if (MotionFlags & PMV_QUICKSTOP16) goto EPZS16_Terminate_without_Refine; if (MotionFlags & PMV_EARLYSTOP16) @@ -2154,10 +2033,10 @@ backupMV = *currMV; /* save best prediction, actually only for EXTSEARCH */ - if (MotionFlags & PMV_USESQUARES8) + if (MotionFlags & PMV_USESQUARES16) MainSearchPtr = Square16_MainSearch; else - if (MotionFlags & PMV_ADVANCEDDIAMOND8) + if (MotionFlags & PMV_ADVANCEDDIAMOND16) MainSearchPtr = AdvDiamond16_MainSearch; else MainSearchPtr = Diamond16_MainSearch; @@ -2287,7 +2166,8 @@ max_dy = EVEN(max_dy); } /* because we might use something like IF (dx>max_dx) THEN dx=max_dx; */ - bPredEq = get_pmvdata(pMBs, x >> 1, y >> 1, iWcount, iSubBlock, pmv, psad); + //bPredEq = get_pmvdata(pMBs, x >> 1, y >> 1, iWcount, iSubBlock, pmv, psad); + bPredEq = get_pmvdata2(pMBs, iWcount, 0, x >> 1, y >> 1, iSubBlock, pmv, psad); /* Step 4: Calculate SAD around the Median prediction. @@ -2467,6 +2347,291 @@ +int32_t +PMVfastIntSearch16(const uint8_t * const pRef, + const uint8_t * const pRefH, + const uint8_t * const pRefV, + const uint8_t * const pRefHV, + const IMAGE * const pCur, + const int x, + const int y, + const uint32_t MotionFlags, + const uint32_t iQuant, + const uint32_t iFcode, + const MBParam * const pParam, + const MACROBLOCK * const pMBs, + const MACROBLOCK * const prevMBs, + VECTOR * const currMV, + VECTOR * const currPMV) +{ + const uint32_t iWcount = pParam->mb_width; + const int32_t iWidth = pParam->width; + const int32_t iHeight = pParam->height; + const int32_t iEdgedWidth = pParam->edged_width; + + const uint8_t *cur = pCur->y + x * 16 + y * 16 * iEdgedWidth; + const VECTOR zeroMV = { 0, 0 }; + + int32_t iDiamondSize; + + int32_t min_dx; + int32_t max_dx; + int32_t min_dy; + int32_t max_dy; + + int32_t iFound; + + VECTOR newMV; + VECTOR backupMV; /* just for PMVFAST */ + + VECTOR pmv[4]; + int32_t psad[4]; + + MainSearch16FuncPtr MainSearchPtr; + + const MACROBLOCK *const prevMB = prevMBs + x + y * iWcount; + MACROBLOCK *const pMB = pMBs + x + y * iWcount; + + int32_t threshA, threshB; + int32_t bPredEq; + int32_t iMinSAD, iSAD; + +/* Get maximum range */ + get_range(&min_dx, &max_dx, &min_dy, &max_dy, x, y, 16, iWidth, iHeight, + iFcode); + +/* we work with abs. MVs, not relative to prediction, so get_range is called relative to 0,0 */ + + if ((x == 0) && (y == 0)) { + threshA = 512; + threshB = 1024; + + bPredEq = 0; + psad[0] = psad[1] = psad[2] = psad[3] = 0; + *currMV = pmv[0] = pmv[1] = pmv[2] = pmv[3] = zeroMV; + + } else { + threshA = psad[0]; + threshB = threshA + 256; + if (threshA < 512) + threshA = 512; + if (threshA > 1024) + threshA = 1024; + if (threshB > 1792) + threshB = 1792; + + bPredEq = get_ipmvdata(pMBs, iWcount, 0, x, y, 0, pmv, psad); + *currMV = pmv[0]; /* current best := prediction */ + } + + iFound = 0; + +/* Step 4: Calculate SAD around the Median prediction. + MinSAD=SAD + If Motion Vector equal to Previous frame motion vector + and MinSADx > max_dx) { + currMV->x = EVEN(max_dx); + } + if (currMV->x < min_dx) { + currMV->x = EVEN(min_dx); + } + if (currMV->y > max_dy) { + currMV->y = EVEN(max_dy); + } + if (currMV->y < min_dy) { + currMV->y = EVEN(min_dy); + } + + iMinSAD = + sad16(cur, + get_ref_mv(pRef, pRefH, pRefV, pRefHV, x, y, 16, currMV, + iEdgedWidth), iEdgedWidth, MV_MAX_ERROR); + iMinSAD += + calc_delta_16(currMV->x - pmv[0].x, currMV->y - pmv[0].y, + (uint8_t) iFcode, iQuant); + + if ((iMinSAD < 256) || + ((MVequal(*currMV, prevMB->i_mvs[0])) && + ((int32_t) iMinSAD < prevMB->i_sad16))) { + if (iMinSAD < 2 * iQuant) // high chances for SKIP-mode + { + if (!MVzero(*currMV)) { + iMinSAD += MV16_00_BIAS; + CHECK_MV16_ZERO; // (0,0) saves space for letterboxed pictures + iMinSAD -= MV16_00_BIAS; + } + } + + if (MotionFlags & PMV_EARLYSTOP16) + goto PMVfastInt16_Terminate_with_Refine; + } + + +/* Step 2 (lazy eval): Calculate Distance= |MedianMVX| + |MedianMVY| where MedianMV is the motion + vector of the median. + If PredEq=1 and MVpredicted = Previous Frame MV, set Found=2 +*/ + + if ((bPredEq) && (MVequal(pmv[0], prevMB->i_mvs[0]))) + iFound = 2; + +/* Step 3 (lazy eval): If Distance>0 or thresb<1536 or PredEq=1 Select small Diamond Search. + Otherwise select large Diamond Search. +*/ + + if ((!MVzero(pmv[0])) || (threshB < 1536) || (bPredEq)) + iDiamondSize = 2; // halfpel units! + else + iDiamondSize = 4; // halfpel units! + +/* + Step 5: Calculate SAD for motion vectors taken from left block, top, top-right, and Previous frame block. + Also calculate (0,0) but do not subtract offset. + Let MinSAD be the smallest SAD up to this point. + If MV is (0,0) subtract offset. +*/ + +// (0,0) is often a good choice + + if (!MVzero(pmv[0])) + CHECK_MV16_ZERO; + +// previous frame MV is always possible + + if (!MVzero(prevMB->i_mvs[0])) + if (!MVequal(prevMB->i_mvs[0], pmv[0])) + CHECK_MV16_CANDIDATE(prevMB->i_mvs[0].x, prevMB->i_mvs[0].y); + +// left neighbour, if allowed + + if (!MVzero(pmv[1])) + if (!MVequal(pmv[1], prevMB->i_mvs[0])) + if (!MVequal(pmv[1], pmv[0])) + CHECK_MV16_CANDIDATE(pmv[1].x, pmv[1].y); + +// top neighbour, if allowed + if (!MVzero(pmv[2])) + if (!MVequal(pmv[2], prevMB->i_mvs[0])) + if (!MVequal(pmv[2], pmv[0])) + if (!MVequal(pmv[2], pmv[1])) + CHECK_MV16_CANDIDATE(pmv[2].x, pmv[2].y); + +// top right neighbour, if allowed + if (!MVzero(pmv[3])) + if (!MVequal(pmv[3], prevMB->i_mvs[0])) + if (!MVequal(pmv[3], pmv[0])) + if (!MVequal(pmv[3], pmv[1])) + if (!MVequal(pmv[3], pmv[2])) + CHECK_MV16_CANDIDATE(pmv[3].x, + pmv[3].y); + + if ((MVzero(*currMV)) && + (!MVzero(pmv[0])) /* && (iMinSAD <= iQuant * 96) */ ) + iMinSAD -= MV16_00_BIAS; + + +/* Step 6: If MinSAD <= thresa goto Step 10. + If Motion Vector equal to Previous frame motion vector and MinSADi_mvs[0]) && + ((int32_t) iMinSAD < prevMB->i_sad16))) { + + if (MotionFlags & PMV_EARLYSTOP16) + goto PMVfastInt16_Terminate_with_Refine; + } + + +/************ (Diamond Search) **************/ +/* + Step 7: Perform Diamond search, with either the small or large diamond. + If Found=2 only examine one Diamond pattern, and afterwards goto step 10 + Step 8: If small diamond, iterate small diamond search pattern until motion vector lies in the center of the diamond. + If center then goto step 10. + Step 9: If large diamond, iterate large diamond search pattern until motion vector lies in the center. + Refine by using small diamond and goto step 10. +*/ + + if (MotionFlags & PMV_USESQUARES16) + MainSearchPtr = Square16_MainSearch; + else if (MotionFlags & PMV_ADVANCEDDIAMOND16) + MainSearchPtr = AdvDiamond16_MainSearch; + else + MainSearchPtr = Diamond16_MainSearch; + + backupMV = *currMV; /* save best prediction, actually only for EXTSEARCH */ + + +/* default: use best prediction as starting point for one call of PMVfast_MainSearch */ + iSAD = + (*MainSearchPtr) (pRef, pRefH, pRefV, pRefHV, cur, x, y, currMV->x, + currMV->y, iMinSAD, &newMV, pmv, min_dx, max_dx, + min_dy, max_dy, iEdgedWidth, iDiamondSize, iFcode, + iQuant, iFound); + + if (iSAD < iMinSAD) { + *currMV = newMV; + iMinSAD = iSAD; + } + + if (MotionFlags & PMV_EXTSEARCH16) { +/* extended: search (up to) two more times: orignal prediction and (0,0) */ + + if (!(MVequal(pmv[0], backupMV))) { + iSAD = + (*MainSearchPtr) (pRef, pRefH, pRefV, pRefHV, cur, x, y, + pmv[0].x, pmv[0].y, iMinSAD, &newMV, pmv, + min_dx, max_dx, min_dy, max_dy, iEdgedWidth, + iDiamondSize, iFcode, iQuant, iFound); + + if (iSAD < iMinSAD) { + *currMV = newMV; + iMinSAD = iSAD; + } + } + + if ((!(MVzero(pmv[0]))) && (!(MVzero(backupMV)))) { + iSAD = + (*MainSearchPtr) (pRef, pRefH, pRefV, pRefHV, cur, x, y, 0, 0, + iMinSAD, &newMV, pmv, min_dx, max_dx, min_dy, + max_dy, iEdgedWidth, iDiamondSize, iFcode, + iQuant, iFound); + + if (iSAD < iMinSAD) { + *currMV = newMV; + iMinSAD = iSAD; + } + } + } + +/* + Step 10: The motion vector is chosen according to the block corresponding to MinSAD. +*/ + +PMVfastInt16_Terminate_with_Refine: + + pMB->i_mvs[0] = pMB->i_mvs[1] = pMB->i_mvs[2] = pMB->i_mvs[3] = pMB->i_mv16 = *currMV; + pMB->i_sad8[0] = pMB->i_sad8[1] = pMB->i_sad8[2] = pMB->i_sad8[3] = pMB->i_sad16 = iMinSAD; + + if (MotionFlags & PMV_HALFPELREFINE16) // perform final half-pel step + iMinSAD = + Halfpel16_Refine(pRef, pRefH, pRefV, pRefHV, cur, x, y, currMV, + iMinSAD, pmv, min_dx, max_dx, min_dy, max_dy, + iFcode, iQuant, iEdgedWidth); + + pmv[0] = get_pmv2(pMBs, pParam->mb_width, 0, x, y, 0); // get _REAL_ prediction (halfpel possible) + +PMVfastInt16_Terminate_without_Refine: + currPMV->x = currMV->x - pmv[0].x; + currPMV->y = currMV->y - pmv[0].y; + return iMinSAD; +} + /* ***********************************************************