/*
* Copyright (c) 2010 Ixonos Plc.
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - Initial contribution
*
* Contributors:
* Ixonos Plc
*
* Description:
* Motion vector calculations and storage.
*
*/
#include "h263dConfig.h"
#include "vdcmvc.h"
#include "core.h"
#include "errcodes.h"
#include "debug.h"
/*
* Defines
*/
#define NEIGHBOUR_SIZE 2 /* The size for neighbour array */
#define MVD_NOT_CODED -32767 /* Macroblock is not coded in mvcTable_t */
#define MVD_INTRA -32765 /* Marks intra macroblock in mvcTable_t */
#define MVD_UMIN_MPEG4 -10240
#define MVD_MIN -160 /* The minimum for mvd. */
#define MVD_MAX 155 /* The maximum for mvd. */
#define MVD_UMIN_V1 -315 /* The minimum for MV in unrestricted mode (v1) */
#define MVD_UMAX_V1 315 /* The maximum for MV in unrestricted mode (v1) */
#define MVD_UMIN_V2 -32000 /* The minimum for MV in unrestricted mode (v2)
Note: this value is used to check the validity
of MVs. If there is no MV for a MB
MVD_NOT_CODED or MVD_INTRA are used, and
therefore any value bigger than those
suits for this definition. In H.263 v2
unlimited submode, MVs can have any length
within a picture (plus 15-pixel border),
and therefore this definition is set to small
enough value. */
#define MVD_155 155 /* MV for 15.5 */
#define MVD_160 160 /* MV for 16 */
#define MVD_320 320 /* MV for 32 */
/* It is assumed that motion vector values are signed and in order
(for example MV for 15.5 < MV for 16).
No other assumptions are made. */
/*
* Macros
*/
/* mvValid checks if the specified item is a valid motion vector */
/* Note: MVD_NOT_CODED and MVD_INTRA are very small negative numbers.
Thus, we can use the minimum MV for H.263 Annex D unlimited
submode to check the validity of a passed item. Actually,
we could use any value larger than MVD_NOT_CODED and MVD_INTRA
and smaller than or equal to the minimum MV. */
#define mvValid(d1item, d1y, d1time) \
(d1item.mvx >= MVD_UMIN_V2 \
&& d1item.y == (d1y) && d1item.time == (d1time))
#define mvValidMPEG(d1item, d1y, d1time) \
(d1item.mvx >= MVD_UMIN_MPEG4 \
&& d1item.y == (d1y) && d1item.time == (d1time))
/* mvLegal cheks if the specified item has been updated to either contain
a motion vector or not coded / intra information */
#define mvLegal(d5item, d5y, d5time) \
(d5item.y == (d5y) && d5item.time == (d5time))
/* mvStore puts given arguments into the motion vector array
Used from: mvcCalcMV */
#define mvStore(d2row, d2x, d2y, d2mvx, d2mvy, d2time, d2type) \
d2row[d2x].mvx = d2mvx; d2row[d2x].mvy = d2mvy; \
d2row[d2x].y = d2y; d2row[d2x].time = d2time; d2row[d2x].fourMVs = 1; \
d2row[d2x].type = d2type
/* mvStoreMB puts given arguments into the motion vector array for the
whole macroblock
Used from: mvcCalcMV, mvcMarkMBIntra, mvcMarkMBNotCoded */
#define mvStoreMB(d4row1, d4row2, d4x, d4y, d4mvx, d4mvy, d4time, d4type) \
d4row1[d4x].mvx = d4row1[d4x + 1].mvx = \
d4row2[d4x].mvx = d4row2[d4x + 1].mvx = d4mvx; \
d4row1[d4x].mvy = d4row1[d4x + 1].mvy = \
d4row2[d4x].mvy = d4row2[d4x + 1].mvy = d4mvy; \
d4row1[d4x].y = d4row1[d4x + 1].y = d4y; \
d4row2[d4x].y = d4row2[d4x + 1].y = d4y + 1; \
d4row1[d4x].time = d4row1[d4x + 1].time = \
d4row2[d4x].time = d4row2[d4x + 1].time = time; \
d4row1[d4x].fourMVs = d4row1[d4x + 1].fourMVs = \
d4row2[d4x].fourMVs = d4row2[d4x + 1].fourMVs = 0; \
d4row1[d4x].type = d4row1[d4x + 1].type = \
d4row2[d4x].type = d4row2[d4x + 1].type = (d4type)
/* updateRowPointers checks if currX, currY or currTime have changed
and updates them. It also updates mvRow0, mvRow1 and mvRow2.
Used from: mvcCalcMV, mvcMarkMBIntra, mvcMarkMBNotCoded */
#define updateRowPointers(d3x, d3y, d3time) \
if (d3x != mvcData->currX) \
mvcData->currX = d3x; \
if (d3y != mvcData->currY) { \
int \
mvRowIndex, \
currMaxX = mvcData->currMaxX; \
mvRowItem_t *mvRow = mvcData->mvRow; \
mvcData->currY = d3y; \
mvcData->mvRowIndex = mvRowIndex = (mvcData->mvRowIndex + 2) % 3; \
mvcData->mvRow0 = &mvRow[(currMaxX + 1) * 2 * mvRowIndex]; \
mvcData->mvRow1 = &mvRow[(currMaxX + 1) * 2 * ((mvRowIndex + 1) % 3)]; \
mvcData->mvRow2 = &mvRow[(currMaxX + 1) * 2 * ((mvRowIndex + 2) % 3)]; \
} \
if (d3time != mvcData->currTime) \
mvcData->currTime = d3time
/* mvcInvMVD takes the other one of the possible MVD values defined in
H.263 Recommendation */
#define mvcInvMVD(invMv) \
(invMv < 0) ? \
MVD_320 + invMv : \
-MVD_320 + invMv
/* sign123(x) returns
1 if x < 0,
2 if x > 0,
3 if x == 0
In other words,
bit 0 = 1 if x <= 0,
bit 1 = 1 if x >= 0 */
#define sign123(x) ((x == 0) ? 3 : (x < 0) ? 1 : 2)
/*
* Local prototypes
*/
static void mvcGetNeighbourMVs(mvcData_t *mvcData, int x, int y, int time,
int *nmvx, int *nmvy, int prevFlag, int16 *error);
__inline static void mvcCheckAndSet(
mvRowItem_t *mvRowPtr, int xind, int yind, int time, int nind,
int *nmvx, int *nmvy, mvRowItem_t *cmvRowPtr, int cxind, int cyind,
int16 *error);
__inline static void mvcSetToCurrent(
mvRowItem_t *mvRowPtr, int xind, int yind, int time, int nind,
int *nmvx, int *nmvy, int16 *error);
/*
* Global functions
*/
/* {{-output"mvcSetBorders.txt"}} */
/*
*
* mvcSetBorders
*
* Parameters:
* mvcData mvcData_t structure
* x the x coordinate of the MB (0 .. maxX)
* y the y coordinate of the MB
* (0 .. macroblock rows in frame - 1)
* mba Macroblock address of starting MB of the slice. This
* value SHOULD be set to -1 if Annex K is not in use.
* numMBsInMBLine Number of MBs in MB line
* rightOfBorder There is a border on the left of the current MB
* downOfBorder There is a border on top of the current MB
*
* Function:
* This function calculates the borders of GOBs and Slices..This function
* SHOULD be called before mvcCalcMV.....
*
* Returns:
* Nothing
*
* Error codes:
* None
*
*/
void mvcSetBorders(
mvcData_t *mvcData, int x, int y, int mba, int numMBsInMBLine,
int *rightOfBorder, int *downOfBorder
)
{
int
upRightMBIsStart;
if (mvcData) {
mvcData->rightOfBorderPrev = mvcData->rightOfBorder;
mvcData->downOfBorderPrev = mvcData->downOfBorder;
}
if (mba == -1) {
if (x == 0) {
*rightOfBorder = 1;
}
else {
*rightOfBorder = 0;
}
/* Always downOfBorder is set to 1, since in QCIF and CIF every GOB
has only 1 MB line. If larger images are supported, numMBLinesInGOB
should be passed here. Also number of starting MB of the GOB may be
needed...*/
*downOfBorder = 1;
upRightMBIsStart = 0;
}
else {
int
xs, /* x coordinate of start MB */
ys; /* y coordinate of start MB */
xs = mba % numMBsInMBLine;
ys = mba / numMBsInMBLine;
/* slice structured mode*/
/* Check if there is slice border on top of the current MB:
For that, check either current MB should be on the same MB line
with the starting MB of slice or current MB should not be on the
left of the starting MB if the MB is on the second line of the
slice. */
if ((y == ys) || ((x < xs) && (ys+1 == y))) {
*downOfBorder = 1;
}
else {
*downOfBorder = 0;
}
/* Check if there is a slice border on the left of the current MB.*/
if (((x == xs) && (y == ys)) || (x == 0)) {
*rightOfBorder = 1;
}
else {
*rightOfBorder = 0;
}
/* Test the situation of MV2 is in another slice but MV3 in in the
same slice... i.e:
MB1 MB1 MB1 MB2 MB2 .........MB2
MB2 MB2 MB2 MB2 MB2..........MB2
*
The MB marked with * is an exmaple. MB on top of it is in another slice
but the MB which MV3 will come from is in the same slice...*/
if ((x+1 == xs) && (y-1 == ys))
upRightMBIsStart = 1;
else
upRightMBIsStart = 0;
}
if (mvcData) {
mvcData->upRightMBIsStart = upRightMBIsStart;
mvcData->downOfBorder = *downOfBorder;
mvcData->rightOfBorder = *rightOfBorder;
mvcData->leftOfBorder = 0;
}
}
/* {{-output"mvcCalcMV.txt"}} */
/*
*
* mvcCalcMV
*
* Parameters:
* mvcData mvcData_t structure
*
* mvdx motion vector data returned by vlcGetMVD
* mvdy
*
* *mvx the resulting motion vector is placed to *mvx and *mvy
* *mvy
*
* predictorMode 0, 1, 2 or 3. See H.263 recommendation, Annex F, Figure
* 15, where modes are in the following order:
* 0 1
* 2 3
*
* Advanced Prediction mode: It is assumed that this
* function is called by setting predictorMode 0, 1, 2
* and 3 in this order for a particular macroblock.
*
* Normal: This function should be called by setting
* predictorMode to 0.
*
* fourMVs if != 0, four motion vectors for current macroblock
* is assumed
*
* unrestrictedMV if != 0, unrestricted motion vector mode is used
*
* topOfGOB if != 0, the current MB is assumed to be in the
* first line of the current GOB
*
* nonEmptyGOBHeader if != 0, the GOB header of the current GOB is
* is assumed to be non-empty, and MV2 and MV3 are
* set to MV1 if they are outside the GOB. If Annex K
* is in use set this value to 1......
*
* x the x coordinate of the MB (0 .. maxX)
* y the y coordinate of the MB
* (0 .. macroblock rows in frame - 1)
*
* time a value which is related to the time when the current
* frame must be shown. This value should be unique
* among a relatively small group of consecutive frames.
*
* mbType must be MVC_MB_INTER or MVC_MB_INTRA. MVC_MB_INTRA
* should be used only in PB frames mode.
*
* error error code
*
* fPLUSPTYPE PLUSTYPE flag
*
* fUMVLimited Annex D v2 flag
* 1 => Limited MVs (UUI = 1)
* 0 => Unlimited MVs (UUI = 01)
*
* Function:
* This function counts the motion vector value for given macroblock.
* See H.263 Recommendation 6.1.1 Differential Motion Vectors and
* Annex F. Advanced Prediction Mode.
*
* Returns:
* Changes *mvx, *mvy and possibly *error.
*
* Error codes:
* ERR_MVC_MVDX_ILLEGAL mvdx illegal
* ERR_MVC_MVDY_ILLEGAL mvdy illegal
* ERR_MVC_MVX_ILLEGAL mvx is NULL
* ERR_MVC_MVY_ILLEGAL mvy is NULL
* ERR_MVC_MODE_ILLEGAL predictorMode illegal
* ERR_MVC_X_ILLEGAL x < 0 or x > maxX
* ERR_MVC_Y_ILLEGAL y < 0
* ERR_MVC_TIME_ILLEGAL time < 0
* ERR_MVC_MVPTR result is not legal
*
*/
void mvcCalcMV(mvcData_t *mvcData, int mvdx, int mvdy,
int *mvx, int *mvy, u_char predictorMode, u_char fourMVs,
u_char unrestrictedMV, u_char nonEmptyGOBHeader, int x,
int y, int time, int mbType, int16 *error, int fPLUSPTYPE,
int fUMVLimited)
/* {{-output"mvcCalcMV.txt"}} */
{
int
i, j, /* loop variables */
mvcx[3], /* Candidates: mv[0] = MV1, mv[1] = MV2, mv[2] = MV3 */
mvcy[3],
*mv, /* mvcx or mvcy */
*mvptr, /* == mvx or mvy */
mvd, /* mvdx or mvdy */
xx2, /* x * 2 */
yx2, /* y * 2 */
xmv1, /* index for MV1 */
xmv2, /* index for MV2 */
xmv3, /* index for MV3 */
ymv1,
ymv2,
ymv3,
mvRangeLow, /* Lower boundary for MV Range
Look H.263+ Rec. Table D.1, D.2 */
mvRangeHigh, /* Higher boundary for MV Range
Look H.263+ Rec. Table D.1, D.2 */
picDim, /* Temporary variable for luminance width or height */
mbCorner, /* Temporary variable for MB's topleft corner's
coordinate (x or y)*/
mvRes; /* MB's rightmost or bottommost pixel plus MV
for checking restriction D.1.1 */
mvRowItem_t
*currMvRow,
*prevMvRow,
*mvRow0,
*mvRow1,
*mvRow2;
/* Check parameters */
if ((!fPLUSPTYPE) || (!unrestrictedMV))
{
if (mvdx < MVD_MIN || mvdx > MVD_MAX) {
*error = ERR_MVC_MVDX_ILLEGAL;
return;
}
if (mvdy < MVD_MIN || mvdy > MVD_MAX) {
*error = ERR_MVC_MVDY_ILLEGAL;
return;
}
}
if (!mvx) {
*error = ERR_MVC_MVX_ILLEGAL;
return;
}
if (!mvy) {
*error = ERR_MVC_MVY_ILLEGAL;
return;
}
if ((fourMVs && predictorMode > 3) ||
(!fourMVs && predictorMode > 0)) {
*error = ERR_MVC_MODE_ILLEGAL;
return;
}
if (x < 0 || x > mvcData->currMaxX) {
*error = ERR_MVC_X_ILLEGAL;
return;
}
if (y < 0) {
*error = ERR_MVC_Y_ILLEGAL;
return;
}
if (time < 0) {
*error = ERR_MVC_TIME_ILLEGAL;
return;
}
updateRowPointers(x, y, time);
mvRow0 = mvcData->mvRow0;
mvRow1 = mvcData->mvRow1;
mvRow2 = mvcData->mvRow2;
/* Seek candidate predictors */
xx2 = x << 1; /* xx2 = x * 2 */
yx2 = y << 1;
xmv1 = xx2 - 1 + (predictorMode & 1);
xmv2 = xx2 + (predictorMode == 1);
xmv3 = xx2 + 2 - (predictorMode > 1);
ymv1 = yx2 + (predictorMode > 1);
ymv2 = ymv3 = yx2 - 1 + (predictorMode > 1);
if (predictorMode <= 1) {
currMvRow = mvRow1;
prevMvRow = mvRow0;
}
else {
currMvRow = mvRow2;
prevMvRow = mvRow1;
}
if ( (predictorMode & 1) || /* right column of the MB */
( !mvcData->rightOfBorder && mvValid(currMvRow[xmv1], ymv1, time) ) ) {
mvcx[0] = currMvRow[xmv1].mvx;
mvcy[0] = currMvRow[xmv1].mvy;
}
else
/* if (x == 0 || motion vector for previous MB does not exist ||
the previous MB belongs to a different slice) */
mvcx[0] = mvcy[0] = 0;
if ((y == 0 || (mvcData->downOfBorder && nonEmptyGOBHeader)) && predictorMode <= 1) {
/* mv2 and mv3 are outside the picture or GOB
(if non-empty GOB header) or outside the slice*/
mvcx[1] = mvcx[0];
mvcy[1] = mvcy[0];
/* Check if the MB ont the previous line and on the next column
in in the same slice (means start MBof the slice).. */
if (!mvcData->upRightMBIsStart) {
mvcx[2] = mvcx[0];
mvcy[2] = mvcy[0];
}
if (x == mvcData->currMaxX)
mvcx[2] = mvcy[2] = 0;
}
else {
if (mvValid(prevMvRow[xmv2], ymv2, time)) {
mvcx[1] = prevMvRow[xmv2].mvx;
mvcy[1] = prevMvRow[xmv2].mvy;
}
else
mvcx[1] = mvcy[1] = 0;
if (mvValid(prevMvRow[xmv3], ymv3, time)) {
mvcx[2] = prevMvRow[xmv3].mvx;
mvcy[2] = prevMvRow[xmv3].mvy;
}
else
mvcx[2] = mvcy[2] = 0;
}
for (j = 0, mv = mvcx, mvd = mvdx, mvptr = mvx,
picDim = mvcData->currLumWidth, mbCorner = x,
mvRangeLow = mvcData->mvRangeLowX,
mvRangeHigh = mvcData->mvRangeHighX;
j < 2;
j++, mv = mvcy, mvd = mvdy, mvptr = mvy,
picDim = mvcData->currLumHeight, mbCorner = y,
mvRangeLow = mvcData->mvRangeLowY,
mvRangeHigh = mvcData->mvRangeHighY) {
int choice1, min, predictor;
/* Find the median of the candidates */
min = mv[0];
predictor = 32767;
for (i = 1; i < 3; i++) {
if (mv[i] <= min) {
predictor = min;
min = mv[i];
continue;
}
if (mv[i] < predictor)
predictor = mv[i];
}
/* Count the new motion vector value */
if (!unrestrictedMV) {
/* Default prediction mode.
Count the legal [-16..15.5] motion vector */
choice1 = predictor + mvd;
*mvptr = (MVD_MIN <= choice1 && choice1 <= MVD_MAX) ?
choice1 :
/*(mvd == 0) ?
predictor :*/ /* index 32, should never happen?! */
(mvd < 0) ?
MVD_320 + choice1 : /* index 0..31: 32 + mvd + predictor */
-MVD_320 + choice1; /* index 33..63: -32 + mvd + predictor */
/* Check that the result is [-16..15.5] */
if (*mvptr < MVD_MIN || *mvptr > MVD_MAX) {
*error = ERR_MVC_MVPTR;
return;
}
}
else if ((-MVD_155 <= predictor && predictor <= MVD_160) && (!fPLUSPTYPE))
/* Unrestricted motion vector mode && -15.5 <= predictor <= 16.
mvd is always valid. */
*mvptr = predictor + mvd;
else if (!fPLUSPTYPE)
{
/* Unrestricted motion vector mode && predictor not in [-15.5, 16]
Result in [-31.5, 31.5] and has the same sign as predictor */
choice1 = predictor + mvd;
*mvptr = (MVD_UMIN_V1 <= choice1 && choice1 <= MVD_UMAX_V1 &&
(sign123(choice1) & sign123(predictor))) ?
choice1 :
/*(mvd == 0) ?
predictor :*/ /* index 32, should never happen!? */
(mvd < 0) ?
MVD_320 + choice1 : /* index 0..31: 32 + mvd + predictor */
-MVD_320 + choice1; /* index 33..63: -32 + mvd + predictor */
/* Check that the result is in the appropriate range. */
if ((predictor < -MVD_155 && *mvptr > 0) ||
(predictor > MVD_160 && *mvptr < 0)) {
*error = ERR_MVC_MVPTR;
return;
}
}
else if (fUMVLimited)
/* fPlusType==1 && UUI == 1*/
{
*mvptr = predictor + mvd;
if ( *mvptr > 0 )
/* mvRes is the leftmost/topmost pixels coordinate of next
macroblock plus 2 times MV */
mvRes = (mbCorner+1)*32 + (*mvptr)/5 ;
else
/* mvRes is the leftmost/topmost pixels coordinate of current
macroblock plus 2 times MV,
half a pixel corresponds to 1 in integer scale */
mvRes = mbCorner*32 + (*mvptr)/5 ;
if ((mvRes < -30) || (mvRes > picDim * 2 + 30))
/* Restriction D.1.1 */
{
*error = ERR_MVC_MVPTR;
deb("Restriction D.1.1\n");
return;
}
if ((*mvptr < mvRangeLow) || (*mvptr > mvRangeHigh))
/* Restriction D.2 */
{
*error = ERR_MVC_MVPTR;
deb("Restriction D.2 \n");
return;
}
}
else /* fPlusType==1 && UUI == 01 */
{
*mvptr = predictor + mvd;
if ( *mvptr > 0 )
mvRes = (mbCorner+1)*32 + (*mvptr)/5;
else
mvRes = mbCorner*32 + (*mvptr)/5;
if ((mvRes < -30) || (mvRes > picDim * 2 + 30))
/* Restriction D.1.1 */
{
*error = ERR_MVC_MVPTR;
deb("Restriction D.1.1\n");
return;
}
}
}
/* Update motion vector buffer arrays */
switch (predictorMode) {
case 0:
if (fourMVs) {
mvStore(mvRow1, xx2, yx2, *mvx, *mvy, time, mbType);
}
else {
mvStoreMB(mvRow1, mvRow2, xx2, yx2, *mvx, *mvy, time, mbType);
}
break;
case 1:
mvStore(mvRow1, xx2 + 1, yx2, *mvx, *mvy, time, mbType);
break;
case 2:
mvStore(mvRow2, xx2, yx2 + 1, *mvx, *mvy, time, mbType);
break;
case 3:
mvStore(mvRow2, xx2 + 1, yx2 + 1, *mvx, *mvy, time, mbType);
break;
}
}
/* {{-output"mvcCalcMPEGMV.txt"}} */
/*
*
* mvcCalcMPEGMV
*
* Parameters:
* mvcData mvcData_t structure
*
* mvdx motion vector data returned by vlcGetMVD
* mvdy
*
* *mvx the resulting motion vector is placed to *mvx and *mvy
* *mvy
*
* predictorMode 0, 1, 2 or 3. See H.263 recommendation, Annex F, Figure
* 15, where modes are in the following order:
* 0 1
* 2 3
*
* Advanced Prediction mode: It is assumed that this
* function is called by setting predictorMode 0, 1, 2
* and 3 in this order for a particular macroblock.
*
* Normal: This function should be called by setting
* predictorMode to 0.
*
* fourMVs if != 0, four motion vectors for current macroblock
* is assumed
*
* topOfVP if != 0, the current MB is assumed to be in the
* first line of the current VideoPacket
*
* leftOfVP if != 0, the current MB is assumed to be the
* first in the VideoPacket
*
* fmv3_out if != 0, the 3rd MB (top, right) used as MV predictor
* is outside of the current VideoPacket
*
* x the x coordinate of the MB (0 .. maxX)
* y the y coordinate of the MB
* (0 .. macroblock rows in frame - 1)
*
* time a value which is related to the time when the current
* frame must be shown. This value should be unique
* among a relatively small group of consecutive frames.
*
* mbType must be MVC_MB_INTER or MVC_MB_INTRA. MVC_MB_INTRA
* should be used only in PB frames mode.
*
* error error code
*
* Function:
* This function counts the motion vector value for given macroblock.
* See H.263 Recommendation 6.1.1 Differential Motion Vectors and
* Annex F. Advanced Prediction Mode.
*
* Returns:
* Changes *mvx, *mvy and possibly *error.
*
* Error codes:
* The following codes are assertion-like checks:
* ERR_MVC_MVDX_ILLEGAL mvdx illegal
* ERR_MVC_MVDY_ILLEGAL mvdy illegal
* ERR_MVC_MVX_ILLEGAL mvx is NULL
* ERR_MVC_MVY_ILLEGAL mvy is NULL
* ERR_MVC_MODE_ILLEGAL predictorMode illegal
* ERR_MVC_X_ILLEGAL x < 0 or x > maxX
* ERR_MVC_Y_ILLEGAL y < 0
* ERR_MVC_TIME_ILLEGAL time < 0
*
* The following code may also be caused by a bit error:
* ERR_MVC_MVPTR result is not legal
*
*/
void mvcCalcMPEGMV(mvcData_t *mvcData,
int mvdx, int mvdy, int *mvx, int *mvy,
u_char predictorMode, u_char fourMVs,
u_char topOfVP, u_char leftOfVP, u_char fmv3_out,
int x, int y, int time, int mbType, int16 *error)
/* {{-output"mvccalc.txt"}} */
{
int
i, j, /* loop variables */
mvcx[3], /* Candidates: mv[0] = MV1, mv[1] = MV2, mv[2] = MV3 */
mvcy[3],
mvc1_out = 0, mvc2_out = 0, mvc3_out = 0,
*mv, /* mvcx or mvcy */
*mvptr, /* == mvx or mvy */
mvd, /* mvdx or mvdy */
xx2, /* x * 2 */
yx2, /* y * 2 */
xmv1, /* index for MV1 */
xmv2, /* index for MV2 */
xmv3, /* index for MV3 */
ymv1,
ymv2,
ymv3;
mvRowItem_t
*currMvRow,
*prevMvRow,
*mvRow0,
*mvRow1,
*mvRow2;
if (!mvx) {
*error = ERR_MVC_MVX_ILLEGAL;
return;
}
if (!mvy) {
*error = ERR_MVC_MVY_ILLEGAL;
return;
}
if ((fourMVs && predictorMode > 3) ||
(!fourMVs && predictorMode > 0)) {
*error = ERR_MVC_MODE_ILLEGAL;
return;
}
if (x < 0 || x > mvcData->currMaxX) {
*error = ERR_MVC_X_ILLEGAL;
return;
}
if (y < 0) {
*error = ERR_MVC_Y_ILLEGAL;
return;
}
if (time < 0) {
*error = ERR_MVC_TIME_ILLEGAL;
return;
}
updateRowPointers(x, y, time);
mvRow0 = mvcData->mvRow0;
mvRow1 = mvcData->mvRow1;
mvRow2 = mvcData->mvRow2;
/* Seek candidate predictors */
xx2 = x << 1; /* xx2 = x * 2 */
yx2 = y << 1;
xmv1 = xx2 - 1 + (predictorMode & 1);
xmv2 = xx2 + (predictorMode == 1);
xmv3 = xx2 + 2 - (predictorMode > 1);
ymv1 = yx2 + (predictorMode > 1);
ymv2 = ymv3 = yx2 - 1 + (predictorMode > 1);
if (predictorMode <= 1) {
currMvRow = mvRow1;
prevMvRow = mvRow0;
}
else {
currMvRow = mvRow2;
prevMvRow = mvRow1;
}
if ((x == 0 || leftOfVP) && !(predictorMode & 1)) {
/* mv1 is outside the VP or VOP */
mvcx[0] = mvcy[0] = 0;
mvc1_out = 1;
}
else {
if (mvValidMPEG(currMvRow[xmv1], ymv1, time)) {
mvcx[0] = currMvRow[xmv1].mvx;
mvcy[0] = currMvRow[xmv1].mvy;
}
else mvcx[0] = mvcy[0] = 0;
}
if ((y == 0 || topOfVP) && predictorMode <= 1) {
/* mv2 is outside the VP or VOP */
mvcx[1] = mvcy[1] = 0;
mvc2_out = 1;
}
else {
if (mvValidMPEG(prevMvRow[xmv2], ymv2, time)) {
mvcx[1] = prevMvRow[xmv2].mvx;
mvcy[1] = prevMvRow[xmv2].mvy;
}
else mvcx[1] = mvcy[1] = 0;
}
if ((y == 0 || fmv3_out || x == mvcData->currMaxX) && predictorMode <= 1) {
/* mv3 is outside the VP or VOP */
mvcx[2] = mvcy[2] = 0;
mvc3_out = 1;
}
else {
if (mvValidMPEG(prevMvRow[xmv3], ymv3, time)) {
mvcx[2] = prevMvRow[xmv3].mvx;
mvcy[2] = prevMvRow[xmv3].mvy;
}
else
mvcx[2] = mvcy[2] = 0;
}
for (j = 0, mv = mvcx, mvd = mvdx, mvptr = mvx;
j < 2;
j++, mv = mvcy, mvd = mvdy, mvptr = mvy) {
int min, predictor;
switch (mvc1_out + mvc2_out + mvc3_out)
{
case 3:
predictor = 0;
break;
case 2:
predictor = mv[0] + mv[1] + mv[2];
break;
case 1:
case 0:
/* Find the median of the candidates */
min = mv[0];
predictor = 32767;
for (i = 1; i < 3; i++) {
if (mv[i] <= min) {
predictor = min;
min = mv[i];
continue;
}
if (mv[i] < predictor)
predictor = mv[i];
}
break;
default:
/* Should never happen */
vdcAssert(0);
predictor = 0;
break;
}
/* Count the new motion vector value */
*mvptr = predictor + mvd;
if (*mvptr < -(mvcData->range))
*mvptr += 2*(mvcData->range);
else if (*mvptr > (mvcData->range)-5)
*mvptr -= 2*(mvcData->range);
}
/* Update motion vector buffer arrays */
switch (predictorMode) {
case 0:
if (fourMVs) {
mvStore(mvRow1, xx2, yx2, *mvx, *mvy, time, mbType);
}
else {
mvStoreMB(mvRow1, mvRow2, xx2, yx2, *mvx, *mvy, time, mbType);
}
break;
case 1:
mvStore(mvRow1, xx2 + 1, yx2, *mvx, *mvy, time, mbType);
break;
case 2:
mvStore(mvRow2, xx2, yx2 + 1, *mvx, *mvy, time, mbType);
break;
case 3:
mvStore(mvRow2, xx2 + 1, yx2 + 1, *mvx, *mvy, time, mbType);
break;
}
}
/* {{-output"mvcFree.txt"}} */
/*
*
* mvcFree
*
* Parameters:
* mvcData mvcData_t structure
*
* Function:
* This function frees the dynamic memory allocated by mvcStart.
* mvcFree should be called at least when exiting the main program.
* Alternatively it can be called whenever the playing a video has
* ended.
*
* Returns:
* Nothing
*
* Error codes:
* None
*
*
*
*
*/
void mvcFree(mvcData_t *mvcData)
/* {{-output"mvcFree.txt"}} */
{
if (mvcData) {
if (mvcData->mvRow) free(mvcData->mvRow);
mvcData->currMaxX = -1;
}
}
/* {{-output"mvcGetCurrentMVs.txt"}} */
/*
*
* mvcGetCurrentMVs
*
* Parameters:
* mvcData mvcData_t structure
* mvx
* mvy
* error error code
*
* Function:
* These functions return the motion vectors of the current
* blocks.
*
* Returns:
* Changes mvx, mvy and possibly error.
*
* Error codes:
*
* ERR_MVC_CURR_NOT_VALID if the motion vectors for the current
* macroblock do not exist
*
* ERR_MVC_CURR_NOT_CODED if the current macroblock was not coded
*
* ERR_MVC_CURR_INTRA if the current macroblock was coded in
* INTRA mode
*
*/
void mvcGetCurrentMVs(mvcData_t *mvcData, int *mvx, int *mvy,
int16 *error)
{
int
xx2 = mvcData->currX << 1,
yx2 = mvcData->currY << 1;
mvRowItem_t
*mvRow1 = mvcData->mvRow1,
*mvRow2 = mvcData->mvRow2;
if (mvValid(mvRow1[xx2], yx2, mvcData->currTime) &&
mvRow1[xx2].type == MVC_MB_INTER) {
mvx[0] = mvRow1[xx2].mvx;
mvx[1] = mvRow1[xx2 + 1].mvx;
mvx[2] = mvRow2[xx2].mvx;
mvx[3] = mvRow2[xx2 + 1].mvx;
mvy[0] = mvRow1[xx2].mvy;
mvy[1] = mvRow1[xx2 + 1].mvy;
mvy[2] = mvRow2[xx2].mvy;
mvy[3] = mvRow2[xx2 + 1].mvy;
}
else {
mvx[0] = mvx[1] = mvx[2] = mvx[3] =
mvy[0] = mvy[1] = mvy[2] = mvy[3] = 0;
if mvLegal(mvRow1[xx2], yx2, mvcData->currTime) {
if (mvRow1[xx2].type == MVC_MB_NOT_CODED) {
/*deb("mvcGetCurrentMVs: ERROR - macroblock not coded.\n");*/
*error = ERR_MVC_CURR_NOT_CODED;
}
else if (mvRow1[xx2].type == MVC_MB_INTRA) {
/*deb("mvcGetCurrentMVs: ERROR - INTRA macroblock.\n");*/
*error = ERR_MVC_CURR_INTRA;
}
else {
/*deb("mvcGetCurrentMVs: ERROR - macroblock not valid.\n");*/
*error = ERR_MVC_CURR_NOT_VALID;
}
}
else {
/*deb("mvcGetCurrentMVs: ERROR - macroblock not valid.\n");*/
*error = ERR_MVC_CURR_NOT_VALID;
}
}
}
/* {{-output"mvcGetNeighbourMVs.txt"}} */
/*
*
* mvcGetCurrNeighbourMVs
* mvcGetPrevNeighbourMVs
*
* Parameters:
* mvcData mvcData_t structure
*
* nmvx pointer to motion vector x component array
* nmvy pointer to motion vector y component array
* The size of the array must be 6. The indexing of the
* neighboring motion vectors goes like this:
* (X = the motion vector which is investigated, i.e.
* the last one decoded (mvcGetCurrNeighbourMVs) or
* the one before the last one (mvcGetPrevNeighbourMVs))
* 2 3
* 0 X X 4
* 1 X X 5
* It is suggested that the following defines are used
* instead of values:
* MVC_XM1_UP (0)
* MVC_XM1_DOWN (1)
* MVC_YM1_LEFT (2)
* MVC_YM1_RIGHT (3)
* MVC_XP1_UP (4)
* MVC_XP1_DOWN (5)
* (M stand for minus and P for plus)
*
* pmvx and pmvy are for mvcGetPrevNeighbourMVs only:
* pmvx pointer to the motion vector x component array of the
* previous macroblock
* pmvy pointer to the motion vector y component array of the
* previous macroblock
* The size of the array must be 4. The indexing is similar
* to normal block indexing inside a macroblock:
* 0 1
* 2 3
*
* error error code
*
* Function:
* These functions return the motion vectors of the neighboring
* blocks (on the left side, above or on the right side) of the
* macroblock which is investigated, i.e.
* - the last one decoded (mvcGetCurrNeighbourMVs) or
* - the one before the last one (mvcGetPrevNeighbourMVs))
*
* The H.263 standard for Overlapped motion compensation for luminance
* (Annex F.3) is followed, that is:
* If one of the surrounding macroblocks was not coded, the
* corresponding remote motion vector is set to zero. If one of the
* surrounding (macro)blocks was coded in INTRA mode, the corresponding
* remote motion vector is replaced by the motion vector for the
* current block expect when in PB-frames mode. In this case (INTRA
* block in PB-frame mode), the INTRA block's motion vector is used.
* If the current block is at the border of the picture and therefore
* a surrounding block is not present, the corresponding remote motion
* vector is replaced by the current motion vector.
*
* mvcGetPrevNeighbourMVs also returns the motion vector for the
* previous macroblock.
*
* Returns:
* Changes nmvx, nmvy, pmvx, pmvy and possibly error.
*
* Error codes:
* ERR_MVC_NO_PREV_MB if the current MB has x coordinate 0 and
* mvcGetPrevNeighbourMVs is called
*
* ERR_MVC_NEIGHBOUR_NOT_VALID if one of the neighboring MVs is not
* valid, i.e. it has not been updated
* for this frame. The MV in nmvx and nmvy
* is set to zero.
*
* for mvcGetPrevNeighbourMVs only:
* ERR_MVC_PREV_NOT_VALID if the motion vectors for the previous
* macroblock do not exist
*
* ERR_MVC_PREV_NOT_CODED if the previous macroblock was not coded
*
* ERR_MVC_PREV_INTRA if the previous macroblock was coded in
* INTRA mode
*
*
*
*
*
*
*
*/
void mvcGetCurrNeighbourMVs(mvcData_t *mvcData, int *nmvx, int *nmvy,
int16 *error)
/* {{-output"mvcGetNeighbourMVs.txt"}} */
{
mvRowItem_t
*mvRow1 = mvcData->mvRow1;
int xx2 = mvcData->currX << 1;
mvcGetNeighbourMVs(mvcData, mvcData->currX, mvcData->currY,
mvcData->currTime, nmvx, nmvy, 0, error);
if (*error) {
if mvLegal(mvRow1[xx2], mvcData->currY << 1, mvcData->currTime) {
if (mvRow1[xx2].type == MVC_MB_NOT_CODED) {
/*deb("mvcGetPrevNeighbourMVs: ERROR - macroblock not coded.\n");*/
*error = ERR_MVC_CURR_NOT_CODED;
}
else if (mvRow1[xx2].type == MVC_MB_INTRA) {
/*deb("mvcGetPrevNeighbourMVs: ERROR - INTRA macroblock.\n");*/
*error = ERR_MVC_CURR_INTRA;
}
else {
/*deb("mvcGetPrevNeighbourMVs: ERROR - macroblock not valid.\n");*/
*error = ERR_MVC_CURR_NOT_VALID;
}
}
else {
/*deb("mvcGetPrevNeighbourMVs: ERROR - macroblock not valid.\n");*/
*error = ERR_MVC_CURR_NOT_VALID;
}
}
}
/* {{-output"mvcGetPrevNeighbourMVs.txt"}} */
void mvcGetPrevNeighbourMVs(mvcData_t *mvcData, int *nmvx, int *nmvy,
int *pmvx, int *pmvy, u_char *fourMVs, int16 *error)
/* {{-output"mvcGetPrevNeighbourMVs.txt"}} */
{
int
currX = mvcData->currX,
currY = mvcData->currY;
mvRowItem_t
*mvRow1 = mvcData->mvRow1,
*mvRow2 = mvcData->mvRow2;
if (currX > 0) {
int xx2 = (currX - 1) << 1;
mvcGetNeighbourMVs(mvcData, currX - 1, currY,
mvcData->currTime, nmvx, nmvy, 1, error);
if (mvValid(mvRow1[xx2], currY << 1, mvcData->currTime) &&
mvRow1[xx2].type == MVC_MB_INTER) {
pmvx[0] = mvRow1[xx2].mvx;
pmvx[1] = mvRow1[xx2 + 1].mvx;
pmvx[2] = mvRow2[xx2].mvx;
pmvx[3] = mvRow2[xx2 + 1].mvx;
pmvy[0] = mvRow1[xx2].mvy;
pmvy[1] = mvRow1[xx2 + 1].mvy;
pmvy[2] = mvRow2[xx2].mvy;
pmvy[3] = mvRow2[xx2 + 1].mvy;
*fourMVs = mvRow1[xx2].fourMVs;
}
else {
pmvx[0] = pmvx[1] = pmvx[2] = pmvx[3] =
pmvy[0] = pmvy[1] = pmvy[2] = pmvy[3] = 0;
*fourMVs = 0;
if mvLegal(mvRow1[xx2], currY << 1, mvcData->currTime) {
if (mvRow1[xx2].type == MVC_MB_NOT_CODED) {
/*deb("mvcGetPrevNeighbourMVs: ERROR - macroblock not coded.\n");*/
*error = ERR_MVC_PREV_NOT_CODED;
}
else if (mvRow1[xx2].type == MVC_MB_INTRA) {
/*deb("mvcGetPrevNeighbourMVs: ERROR - INTRA macroblock.\n");*/
*error = ERR_MVC_PREV_INTRA;
}
else {
/*deb("mvcGetPrevNeighbourMVs: ERROR - macroblock not valid.\n");*/
*error = ERR_MVC_PREV_NOT_VALID;
}
}
else {
/*deb("mvcGetPrevNeighbourMVs: ERROR - macroblock not valid.\n");*/
*error = ERR_MVC_PREV_NOT_VALID;
}
}
}
else {
/*deb("mvcGetPrevNeighbourMVs: ERROR - no previous macroblock.\n");*/
*error = ERR_MVC_NO_PREV_MB;
}
}
/* {{-output"mvcGetPrevMVFsAndMVBs.txt"}} */
/*
*
* mvcGetPrevMVFsAndMVBs
*
* Parameters:
* mvcData mvcData_t structure
*
* mvfx the resulting forward motion vector
* mvfy
*
* mvbx the resulting backward motion vector
* mvby
*
* fourMVs 1 if there is four motion vectors per macroblock
* 0 otherwise
*
* Function:
* This function gets the forward and backward motion vectors for the
* previous B macroblock.
*
* Returns:
* Changes *mvfx, *mvfy, *mvbx, *mvby and *fourMVs.
*
* Error codes:
* ERR_MVC_NO_PREV_MB if the current MB has x coordinate 0 and
* mvcGetPrevNeighbourMVs is called
*
* ERR_MVC_PREV_NOT_VALID if the motion vectors for the previous
* macroblock do not exist
*
*
*/
void mvcGetPrevMVFsAndMVBs(mvcData_t *mvcData, int *mvfx, int *mvfy,
int *mvbx, int *mvby, u_char *fourMVs, int16 *error)
/* {{-output"mvcGetPrevMVFsAndMVBs.txt"}} */
{
mvBFBufItem_t
*mvFBuf = mvcData->mvFBufArray[mvcData->mvBFBufIndex ^ 1],
*mvBBuf = mvcData->mvBBufArray[mvcData->mvBFBufIndex ^ 1];
int i;
if (mvcData->currX > 0) {
if (mvBBuf[0].x == mvcData->currX - 1 &&
mvValid(mvBBuf[0], mvcData->currY, mvcData->currTime)) {
*fourMVs = mvBBuf[0].fourMVs;
for (i = 0; i < 4; i++) {
mvfx[i] = mvFBuf[i].mvx;
mvfy[i] = mvFBuf[i].mvy;
mvbx[i] = mvBBuf[i].mvx;
mvby[i] = mvBBuf[i].mvy;
}
}
else {
/*deb("mvcGetPrevMVFsAndMVBs: ERROR - PREV_NOT_VALID.\n");*/
*error = ERR_MVC_PREV_NOT_VALID;
}
}
else {
/*deb("mvcGetPrevMVFsAndMVBs: ERROR - NO_PREV_MB.\n");*/
*error = ERR_MVC_NO_PREV_MB;
}
}
/* {{-output"mvcMarkMB.txt"}} */
/*
*
* mvcMarkMBIntra
* mvcMarkMBNotCoded
*
* Parameters:
* mvcData mvcData_t structure
* x the x coordinate of the MB (0 .. maxX)
* y the y coordinate of the MB
* (0 .. macroblock rows in frame - 1)
*
* time a value which is related to the time when the current
* frame must be shown. This value should be unique
* among a relatively small group of consecutive frames.
*
* Function:
* These functions are used to mark that the macroblock is either
* intra coded or not coded at all. The information is used when
* deciding the neighboring motion vectors of a macroblock in
* mvcGetPrevNeighbourMVs and mvcGetCurrNeighbourMVs.
* Note that mvcMarkMBIntra should not be called in case of
* INTRA block in PB-frame mode. Instead mvcCalcMV should be used.
*
* Returns:
* Nothing
*
* Error codes:
* None
*
*
*
*/
void mvcMarkMBIntra(mvcData_t *mvcData, int x, int y, int time)
/* {{-output"mvcMarkMB.txt"}} */
{
int
xx2 = x << 1,
yx2 = y << 1;
mvRowItem_t
*mvRow1,
*mvRow2;
updateRowPointers(x, y, time);
mvRow1 = mvcData->mvRow1;
mvRow2 = mvcData->mvRow2;
mvStoreMB(mvRow1, mvRow2, xx2, yx2, MVD_INTRA, MVD_INTRA, time, MVC_MB_INTRA);
}
/* {{-output"mvcMarkMBNotCoded.txt"}} */
void mvcMarkMBNotCoded(mvcData_t *mvcData, int x, int y, int time)
/* {{-output"mvcMarkMBNotCoded.txt"}} */
{
int
xx2 = x << 1,
yx2 = y << 1;
mvRowItem_t
*mvRow1,
*mvRow2;
updateRowPointers(x, y, time);
mvRow1 = mvcData->mvRow1;
mvRow2 = mvcData->mvRow2;
mvStoreMB(mvRow1, mvRow2, xx2, yx2, MVD_NOT_CODED,
MVD_NOT_CODED, time, MVC_MB_NOT_CODED);
}
/* {{-output"mvcStart.txt"}} */
/*
*
* mvcStart
*
* Parameters:
* mvcData mvcData_t structure
*
* maxX the largest x coordinate possible for a macroblock
* If maxX if different from maxX when the function was
* last called, a new dynamic memory allocation is made.
*
* lumWidth Luminance width
*
* lumHeight Luminance height
*
* error error code
*
* Function:
* This function initialises motion vector buffers. It also allocates
* buffer memory if needed.
* One should call mvcStart in the beginning of each video sequence.
*
* Returns:
* Nothing
*
* Error codes:
* ERR_MVC_ALLOC1 dynamic allocation error
* ERR_MVC_MAX_X_ILLEGAL maxX <= 0
*
*
*
*
*/
void mvcStart(mvcData_t *mvcData, int maxX, int lumWidth, int lumHeight, int16 *error)
/* {{-output"mvcStart.txt"}} */
{
int
i, j, /* loop variables */
rowSize; /* the number of items in prevMV?Row */
if (maxX < 0) {
*error = ERR_MVC_MAX_X_ILLEGAL;
return;
}
rowSize = (maxX + 1) << 1;
if (mvcData->currMaxX != maxX || mvcData->mvRow == NULL) {
/* frame size changed */
if (mvcData->mvRow) free(mvcData->mvRow);
mvcData->mvRow = (mvRowItem_t *) vdcMalloc(
rowSize * 3 * sizeof(mvRowItem_t));
if (mvcData->mvRow == NULL) {
deb("mvcStart: ERROR - memory allocation failed.\n");
*error = ERR_MVC_ALLOC1;
return;
}
mvcData->currMaxX = maxX;
}
/* Horizontal motion vector range when PLUSTYPE present and UUI = 1
See Table D.1/H.263 */
if (mvcData->currLumHeight != lumHeight)
{
mvcData->currLumHeight = lumHeight;
if ((lumHeight>=4)&&(lumHeight<=288))
{
mvcData->mvRangeLowY = -320;
mvcData->mvRangeHighY = 315;
}
else if ((lumHeight>=292)&&(lumHeight<=576))
{
mvcData->mvRangeLowY = -640;
mvcData->mvRangeHighY = 635;
}
else if ((lumHeight>=580)&&(lumHeight<=1152))
{
mvcData->mvRangeLowY = -1280;
mvcData->mvRangeHighY = 1275;
}
}
/* Vertical motion vector range when PLUSTYPE present and UUI = 1
See Table D.2/H.263 */
if (mvcData->currLumWidth != lumWidth)
{
mvcData->currLumWidth = lumWidth;
if ((lumWidth>=4)&&(lumWidth<=352))
{
mvcData->mvRangeLowX = -320;
mvcData->mvRangeHighX = 315;
}
else if ((lumWidth>=356)&&(lumWidth<=704))
{
mvcData->mvRangeLowX = -640;
mvcData->mvRangeHighX = 635;
}
else if ((lumWidth>=708)&&(lumWidth<=1408))
{
mvcData->mvRangeLowX = -1280;
mvcData->mvRangeHighX = 1275;
}
else if ((lumWidth>=1412)&&(lumWidth<=2048))
{
mvcData->mvRangeLowX = -2560;
mvcData->mvRangeHighX = 2555;
}
}
/* Set time to be impossible */
for (i = 0; i < 3 * rowSize; i++) {
mvcData->mvRow[i].time = -2;
}
for (j = 0; j < 2; j++) {
for (i = 0; i < 4; i++) {
mvcData->mvFBufArray[j][i].time = -2;
mvcData->mvBBufArray[j][i].time = -2;
}
}
mvcData->currX = -1;
mvcData->currY = -1;
mvcData->currTime = -2;
mvcData->mvRowIndex = 0;
mvcData->mvBFBufIndex = 0;
mvcData->prevPredMode = 3;
}
/* Local functions */
/*
*
* mvcGetNeighbourMVs
*
* Parameters:
* mvcData mvcData_t structure
* x
* y macroblock coordinates
* time time reference
* nmvx pointer to motion vector x component array
* nmvy pointer to motion vector y component array
* See mvcGetCurrNeighbourMVs/mvcGetPrevNeighbourMVs for
* description.
* prevFlag If this flag is set, the previous border values should
* be used.
* error error code
*
* Function:
* This function return the motion vectors of the neighboring
* blocks (on the left side, above or on the right side) of the
* macroblock which is investigated. mvcGetNeighbourMVs is used
* by mvcGetCurrNeighbourMVs and mvcGetPrevNeighbourMVs.
* See also the functional description for these functions.
*
* Returns:
* Changes nmvx and nmvy and possibly error.
*
* Error codes:
* ERR_MVC_NEIGHBOUR_NOT_VALID if one of the neighboring MVs is not
* valid, i.e. it has not been updated
* for this frame. The MV in nmvx and nmvy
* is set to zero.
*
*
*/
static void mvcGetNeighbourMVs(mvcData_t *mvcData, int x, int y, int time,
int *nmvx, int *nmvy, int prevFlag, int16 *error)
{
int
xx2 = x << 1,
yx2 = y << 1,
xtmp,
ytmp,
rightOfBorder,
downOfBorder,
leftOfBorder;
mvRowItem_t
*mvRow0 = mvcData->mvRow0,
*mvRow1 = mvcData->mvRow1,
*mvRow2 = mvcData->mvRow2;
if (prevFlag) {
rightOfBorder = mvcData->rightOfBorderPrev;
downOfBorder = (mvcData->fSS)?mvcData->downOfBorderPrev:(y==0);
leftOfBorder = 0;
}
else {
rightOfBorder = mvcData->rightOfBorder;
downOfBorder = (mvcData->fSS)?mvcData->downOfBorder:(y==0);
leftOfBorder = (mvcData->fSS)?(x == mvcData->currMaxX)||(mvcData->leftOfBorder):(x == mvcData->currMaxX);
}
//if (x > 0) {
if (!rightOfBorder) {
xtmp = xx2 - 1;
mvcCheckAndSet(mvRow1, xtmp, yx2, time, MVC_XM1_UP, nmvx, nmvy,
mvRow1, xx2, yx2, error);
mvcCheckAndSet(mvRow2, xtmp, yx2 + 1, time, MVC_XM1_DOWN, nmvx, nmvy,
mvRow2, xx2, yx2 + 1, error);
}
else {
mvcSetToCurrent(mvRow1, xx2, yx2, time, MVC_XM1_UP, nmvx, nmvy,
error);
mvcSetToCurrent(mvRow2, xx2, yx2 + 1, time, MVC_XM1_DOWN, nmvx, nmvy,
error);
}
//if (y > 0) {
if (!downOfBorder) {
ytmp = yx2 - 1;
mvcCheckAndSet(mvRow0, xx2, ytmp, time, MVC_YM1_LEFT, nmvx, nmvy,
mvRow1, xx2, yx2, error);
mvcCheckAndSet(mvRow0, xx2 + 1, ytmp, time, MVC_YM1_RIGHT, nmvx, nmvy,
mvRow1, xx2 + 1, yx2, error);
}
else {
mvcSetToCurrent(mvRow1, xx2, yx2, time, MVC_YM1_LEFT, nmvx, nmvy,
error);
mvcSetToCurrent(mvRow1, xx2 + 1, yx2, time, MVC_YM1_RIGHT, nmvx, nmvy,
error);
}
//if (x < mvcData->currMaxX) {
if (!leftOfBorder) {
xtmp = xx2 + 2;
mvcCheckAndSet(mvRow1, xtmp, yx2, time, MVC_XP1_UP, nmvx, nmvy,
mvRow1, xtmp - 1, yx2, error);
mvcCheckAndSet(mvRow2, xtmp, yx2 + 1, time, MVC_XP1_DOWN, nmvx, nmvy,
mvRow2, xtmp -1, yx2 + 1, error);
}
else {
xtmp = xx2 + 1;
mvcSetToCurrent(mvRow1, xtmp, yx2, time, MVC_XP1_UP, nmvx, nmvy,
error);
mvcSetToCurrent(mvRow2, xtmp, yx2 + 1, time, MVC_XP1_DOWN, nmvx, nmvy,
error);
}
}
/*
* mvcCheckAndSet
*
* Parameters:
* mvRowPtr Source row item pointer
* xind x index of the source block
* yind y index of the source block
* timeref time reference
* nind index to motion vector arrays ( x and y)
* nmvx pointer to motion vector x component array
* nmvy pointer to motion vector y component array
* cmvRowPtr Target row item pointer
* cxind x index of the target block
* cyind y index of the target block
* error error code
*
* Function:
* Sets the correct value for neighboring motion vector
* used from: mvcGetNeighbourMVs
*
* Returns:
* Nothing
*
* Error codes:
*
*/
__inline static void mvcCheckAndSet(
mvRowItem_t *mvRowPtr, int xind, int yind, int timeref, int nind,
int *nmvx, int *nmvy, mvRowItem_t *cmvRowPtr, int cxind, int cyind,
int16 *error)
{
if (mvLegal(mvRowPtr[xind], yind, timeref)) {
if (mvRowPtr[xind].mvx >= MVD_UMIN_V2) {
nmvx[nind] = mvRowPtr[xind].mvx;
nmvy[nind] = mvRowPtr[xind].mvy;
}
else if (mvRowPtr[xind].mvx == MVD_INTRA) {
if (mvLegal(cmvRowPtr[cxind], cyind, timeref)) {
if (mvValid(cmvRowPtr[cxind], cyind, timeref)) {
nmvx[nind] = cmvRowPtr[cxind].mvx;
nmvy[nind] = cmvRowPtr[cxind].mvy;
}
else /* Not coded macroblock */
nmvx[nind] = nmvy[nind] = 0;
}
else {
deb("mvcCheckAndSet: ERROR - neighbour not valid.\n");
*error = ERR_MVC_NEIGHBOUR_NOT_VALID;
nmvx[nind] = nmvy[nind] = 0;
}
}
else { /* MVD_NOT_CODED */
nmvx[nind] = nmvy[nind] = 0;
}
}
else {
deb("mvcCheckAndSet: ERROR - neighbour not valid.\n");
*error = ERR_MVC_NEIGHBOUR_NOT_VALID;
nmvx[nind] = nmvy[nind] = 0;
}
}
/*
* mvcSetToCurrent
*
* Parameters:
* mvRowPtr Current row item pointer
* xind x index of the block
* yind y index of the block
* timeref time reference
* nind index to motion vector arrays ( x and y)
* nmvx pointer to motion vector x component array
* nmvy pointer to motion vector y component array
* error error code
*
* Function:
* Sets the correct value for neighboring motion vector using current
* motion vector, used in cases of border used from: mvcGetNeighbourMVs
*
* Returns:
* Nothing
*
* Error codes:
*
* History
*/
__inline static void mvcSetToCurrent(
mvRowItem_t *mvRowPtr, int xind, int yind, int timeref, int nind,
int *nmvx, int *nmvy, int16 *error)
{
if (mvValid(mvRowPtr[xind], yind, timeref)) {
nmvx[nind] = mvRowPtr[xind].mvx;
nmvy[nind] = mvRowPtr[xind].mvy;
}
else {
deb("mvcSetToCurrent: ERROR - neighbour not valid.\n");
*error = ERR_MVC_NEIGHBOUR_NOT_VALID;
nmvx[nind] = nmvy[nind] = 0;
}
}
// End of File