考虑到帧间相关性,只需要编码视频内动态变化的信息(视频内对象的运动信息,MV,运动矢量)即可大幅度减少所需编码的比特数。
另一方面,准确分割运动对象是非常复杂的,视频编码标准的运动估计是基于像素块进行的(精确到像素级的话复杂度太大)。
X265中在Search::predInterSearch中实现运动估计,返回运动矢量和参考帧的序号。
/* find the best inter prediction for each PU of specified mode */
void Search::predInterSearch(Mode& interMode, const CUGeom& cuGeom, bool bChromaMC, uint32_t refMasks[2])
{
......
if (m_param->analysisReuseLevel == 10 && m_param->interRefine > 1)
{
interDataCTU = m_frame->m_analysisData.interData;
if ((cu.m_predMode[pu.puAbsPartIdx] == interDataCTU->modes[cuIdx + pu.puAbsPartIdx])
&& (cu.m_partSize[pu.puAbsPartIdx] == interDataCTU->partSize[cuIdx + pu.puAbsPartIdx])
&& !(interDataCTU->mergeFlag[cuIdx + puIdx])
&& (cu.m_cuDepth[0] == interDataCTU->depth[cuIdx]))
useAsMVP = true;
}
/* find best cost merge candidate. note: 2Nx2N merge and bidir are handled as separate modes */
uint32_t mrgCost = numPart == 1 ? MAX_UINT : mergeEstimation(cu, cuGeom, pu, puIdx, merge);
bestME[0].cost = MAX_UINT;
bestME[1].cost = MAX_UINT;
getBlkBits((PartSize)cu.m_partSize[0], slice->isInterP(), puIdx, lastMode, m_listSelBits);
bool bDoUnidir = true;
cu.getNeighbourMV(puIdx, pu.puAbsPartIdx, interMode.interNeighbours);
/* Uni-directional prediction */
if ((m_param->analysisLoad && m_param->analysisReuseLevel > 1 && m_param->analysisReuseLevel != 10)
|| (m_param->analysisMultiPassRefine && m_param->rc.bStatRead) || (m_param->bAnalysisType == AVC_INFO) || (useAsMVP))
{
for (int list = 0; list < numPredDir; list++)
{
......
}
}
else if (m_param->bDistributeMotionEstimation)
{
......
/* if no peer threads were bonded, fall back to doing unidirectional
* searches ourselves without overhead of singleMotionEstimation() */
}
if (bDoUnidir)//默认会进这里执行单向帧间预测
{
interMode.bestME[puIdx][0].ref = interMode.bestME[puIdx][1].ref = -1;
uint32_t refMask = refMasks[puIdx] ? refMasks[puIdx] : (uint32_t)-1;
for (int list = 0; list < numPredDir; list++)
{
for (int ref = 0; ref < numRefIdx[list]; ref++)
{
ProfileCounter(interMode.cu, totalMotionReferences[cuGeom.depth]);
if (!(refMask & (1 << ref)))
{
ProfileCounter(interMode.cu, skippedMotionReferences[cuGeom.depth]);
continue;
}
uint32_t bits = m_listSelBits[list] + MVP_IDX_BITS;
bits += getTUBits(ref, numRefIdx[list]);
int numMvc = cu.getPMV(interMode.interNeighbours, list, ref, interMode.amvpCand[list][ref], mvc);
const MV* amvp = interMode.amvpCand[list][ref];
int mvpIdx = selectMVP(cu, pu, amvp, list, ref);
MV mvmin, mvmax, outmv, mvp = amvp[mvpIdx], mvp_lowres;
bool bLowresMVP = false;
if (!m_param->analysisSave && !m_param->analysisLoad) /* Prevents load/save outputs from diverging when lowresMV is not available */
{
MV lmv = getLowresMV(cu, pu, list, ref);
if (lmv.notZero())
mvc[numMvc++] = lmv;
if (m_param->bEnableHME)
mvp_lowres = lmv;
}
if (m_param->searchMethod == X265_SEA)
{
int puX = puIdx & 1;
int puY = puIdx >> 1;
for (int planes = 0; planes < INTEGRAL_PLANE_NUM; planes++)
m_me.integral[planes] = interMode.fencYuv->m_integral[list][ref][planes] + puX * pu.width + puY * pu.height * m_slice->m_refFrameList[list][ref]->m_reconPic->m_stride;
}
setSearchRange(cu, mvp, m_param->searchRange, mvmin, mvmax);//估计searchRange计算MV的范围
int satdCost = m_me.motionEstimate(&slice->m_mref[list][ref], mvmin, mvmax, mvp, numMvc, mvc, m_param->searchRange, outmv, m_param->maxSlices,
m_param->bSourceReferenceEstimation ? m_slice->m_refFrameList[list][ref]->m_fencPic->getLumaAddr(0) : 0);//执行实际运动估计
if (m_param->bEnableHME && mvp_lowres.notZero() && mvp_lowres != mvp)
{
MV outmv_lowres;
setSearchRange(cu, mvp_lowres, m_param->searchRange, mvmin, mvmax);
int lowresMvCost = m_me.motionEstimate(&slice->m_mref[list][ref], mvmin, mvmax, mvp_lowres, numMvc, mvc, m_param->searchRange, outmv_lowres, m_param->maxSlices,
m_param->bSourceReferenceEstimation ? m_slice->m_refFrameList[list][ref]->m_fencPic->getLumaAddr(0) : 0);
if (lowresMvCost < satdCost)
{
outmv = outmv_lowres;
satdCost = lowresMvCost;
bLowresMVP = true;
}
}
/* Get total cost of partition, but only include MV bit cost once */
bits += m_me.bitcost(outmv);
uint32_t mvCost = m_me.mvcost(outmv);
uint32_t cost = (satdCost - mvCost) + m_rdCost.getCost(bits);
/* Update LowresMVP to best AMVP cand*/
if (bLowresMVP)
updateMVP(amvp[mvpIdx], outmv, bits, cost, mvp_lowres);//更新最佳MVP
/* Refine MVP selection, updates: mvpIdx, bits, cost */
mvp = checkBestMVP(amvp, outmv, mvpIdx, bits, cost);
if (cost < bestME[list].cost)
{
bestME[list].mv = outmv;
bestME[list].mvp = mvp;
bestME[list].mvpIdx = mvpIdx;
bestME[list].ref = ref;
bestME[list].cost = cost;
bestME[list].bits = bits;
bestME[list].mvCost = mvCost;
}
}
/* the second list ref bits start at bit 16 */
refMask >>= 16;
}
}
/* Bi-directional prediction */
MotionData bidir[2];
uint32_t bidirCost = MAX_UINT;
int bidirBits = 0;
if (slice->isInterB() && !cu.isBipredRestriction() && /* biprediction is possible for this PU */ B帧支持双向预测
cu.m_partSize[pu.puAbsPartIdx] != SIZE_2Nx2N && /* 2Nx2N biprediction is handled elsewhere */
bestME[0].cost != MAX_UINT && bestME[1].cost != MAX_UINT)
{
bidir[0] = bestME[0];
bidir[1] = bestME[1];
int satdCost;
if (m_me.bChromaSATD)
{
......
motionCompensation(cu, pu, tmpPredYuv, true, true);
satdCost = m_me.bufSATD(tmpPredYuv.getLumaAddr(pu.puAbsPartIdx), tmpPredYuv.m_size) +
m_me.bufChromaSATD(tmpPredYuv, pu.puAbsPartIdx);
}
else
{
PicYuv* refPic0 = slice->m_refReconPicList[0][bestME[0].ref];
PicYuv* refPic1 = slice->m_refReconPicList[1][bestME[1].ref];
Yuv* bidirYuv = m_rqt[cuGeom.depth].bidirPredYuv;
/* Generate reference subpels */
predInterLumaPixel(pu, bidirYuv[0], *refPic0, bestME[0].mv);
predInterLumaPixel(pu, bidirYuv[1], *refPic1, bestME[1].mv);
primitives.pu[m_me.partEnum].pixelavg_pp[(tmpPredYuv.m_size % 64 == 0) && (bidirYuv[0].m_size % 64 == 0) && (bidirYuv[1].m_size % 64 == 0)](tmpPredYuv.m_buf[0], tmpPredYuv.m_size, bidirYuv[0].getLumaAddr(pu.puAbsPartIdx), bidirYuv[0].m_size,
bidirYuv[1].getLumaAddr(pu.puAbsPartIdx), bidirYuv[1].m_size, 32);
satdCost = m_me.bufSATD(tmpPredYuv.m_buf[0], tmpPredYuv.m_size);
}
bidirBits = bestME[0].bits + bestME[1].bits + m_listSelBits[2] - (m_listSelBits[0] + m_listSelBits[1]);
bidirCost = satdCost + m_rdCost.getCost(bidirBits);
bool bTryZero = bestME[0].mv.notZero() || bestME[1].mv.notZero();
if (bTryZero)
{
/* Do not try zero MV if unidir motion predictors are beyond
* valid search area */
......
}
if (bTryZero)
{
/* coincident blocks of the two reference pictures */
......
}
}
/* select best option and store into CU */
if (mrgCost < bidirCost && mrgCost < bestME[0].cost && mrgCost < bestME[1].cost)
{
......
totalmebits += merge.bits;
}
else if (bidirCost < bestME[0].cost && bidirCost < bestME[1].cost)
{
lastMode = 2;
......
totalmebits += bidirBits;
}
else if (bestME[0].cost <= bestME[1].cost)
{
lastMode = 0;
......
totalmebits += bestME[0].bits;
}
else
{
lastMode = 1;
......
totalmebits += bestME[1].bits;
}
motionCompensation(cu, pu, *predYuv, true, bChromaMC);//运动估计结束完之后做运动补偿
}
interMode.sa8dBits += totalmebits;
}
实际的运动估计是在MotionEstimate::motionEstimate中实现的
int MotionEstimate::motionEstimate(ReferencePlanes *ref,
const MV & mvmin,
const MV & mvmax,
const MV & qmvp,
int numCandidates,
const MV * mvc,
int merange,
MV & outQMv,
uint32_t maxSlices,
pixel * srcReferencePlane)
{
......
MV omv = bmv; // current search origin or starting point
int search = ref->isHMELowres ? (hme ? searchMethodL0 : searchMethodL1) : searchMethod;
switch (search)
{
case X265_DIA_SEARCH:
{
/* diamond search, radius 1 */
......
}
//X265默认的搜索方式,先六边形搜索,再正方形搜索细化
case X265_HEX_SEARCH:
{
me_hex2:
/* hexagon search, radius 2 */
......
COST_MV_X3_DIR(-2, 0, -1, 2, 1, 2, costs);//计算六边形左,左上,右上三个点的cost
bcost <<= 3;左移3位,低3位存放位置信息
if ((bmv.y >= mvmin.y) & (bmv.y <= mvmax.y))
COPY1_IF_LT(bcost, (costs[0] << 3) + 2);
if ((bmv.y + 2 >= mvmin.y) & (bmv.y + 2 <= mvmax.y))
{
COPY1_IF_LT(bcost, (costs[1] << 3) + 3);
COPY1_IF_LT(bcost, (costs[2] << 3) + 4);
}//不超过搜索范围的点比较并存储位置信息
COST_MV_X3_DIR(2, 0, 1, -2, -1, -2, costs);//计算六边形右,右下,左下三个点的cost
if ((bmv.y >= mvmin.y) & (bmv.y <= mvmax.y))
COPY1_IF_LT(bcost, (costs[0] << 3) + 5);
if ((bmv.y - 2 >= mvmin.y) & (bmv.y - 2 <= mvmax.y))
{
COPY1_IF_LT(bcost, (costs[1] << 3) + 6);
COPY1_IF_LT(bcost, (costs[2] << 3) + 7);
}不超过搜索范围的点比较并存储位置信息,和上面的过程对称
if (bcost & 7)//取末尾3位,得到位置信息,判断是否存在cost更小的点
{
int dir = (bcost & 7) - 2;//获取相当于左偏移的方向
if ((bmv.y + hex2[dir + 1].y >= mvmin.y) & (bmv.y + hex2[dir + 1].y <= mvmax.y))
{
bmv += hex2[dir + 1];//新的中心点位置
/* half hexagon, not overlapping the previous iteration */
for (int i = (merange >> 1) - 1; i > 0 && bmv.checkRange(mvmin, mvmax); i--)
{
COST_MV_X3_DIR(hex2[dir + 0].x, hex2[dir + 0].y,
hex2[dir + 1].x, hex2[dir + 1].y,
hex2[dir + 2].x, hex2[dir + 2].y,
costs);//只计算一半,因为新的六边形有3个点在上一次已经计算过了
bcost &= ~7;//清掉低位位置信息
if ((bmv.y + hex2[dir + 0].y >= mvmin.y) & (bmv.y + hex2[dir + 0].y <= mvmax.y))
COPY1_IF_LT(bcost, (costs[0] << 3) + 1);
if ((bmv.y + hex2[dir + 1].y >= mvmin.y) & (bmv.y + hex2[dir + 1].y <= mvmax.y))
COPY1_IF_LT(bcost, (costs[1] << 3) + 2);
if ((bmv.y + hex2[dir + 2].y >= mvmin.y) & (bmv.y + hex2[dir + 2].y <= mvmax.y))
COPY1_IF_LT(bcost, (costs[2] << 3) + 3);
if (!(bcost & 7))
break;
dir += (bcost & 7) - 2;
dir = mod6m1[dir + 1];
bmv += hex2[dir + 1];
}
}
}
bcost >>= 3;
/* square refine */
int dir = 0;
COST_MV_X4_DIR(0, -1, 0, 1, -1, 0, 1, 0, costs);
if ((bmv.y - 1 >= mvmin.y) & (bmv.y - 1 <= mvmax.y))
COPY2_IF_LT(bcost, costs[0], dir, 1);
if ((bmv.y + 1 >= mvmin.y) & (bmv.y + 1 <= mvmax.y))
COPY2_IF_LT(bcost, costs[1], dir, 2);
COPY2_IF_LT(bcost, costs[2], dir, 3);
COPY2_IF_LT(bcost, costs[3], dir, 4);
COST_MV_X4_DIR(-1, -1, -1, 1, 1, -1, 1, 1, costs);
if ((bmv.y - 1 >= mvmin.y) & (bmv.y - 1 <= mvmax.y))
COPY2_IF_LT(bcost, costs[0], dir, 5);
if ((bmv.y + 1 >= mvmin.y) & (bmv.y + 1 <= mvmax.y))
COPY2_IF_LT(bcost, costs[1], dir, 6);
if ((bmv.y - 1 >= mvmin.y) & (bmv.y - 1 <= mvmax.y))
COPY2_IF_LT(bcost, costs[2], dir, 7);
if ((bmv.y + 1 >= mvmin.y) & (bmv.y + 1 <= mvmax.y))
COPY2_IF_LT(bcost, costs[3], dir, 8);
bmv += square1[dir];//在此基础上做进一步正方形搜索
break;
}
case X265_UMH_SEARCH:
{
.......
}
case X265_STAR_SEARCH: // Adapted from HM ME
{
......
}
case X265_SEA:
{
// Successive Elimination Algorithm
.......
}
case X265_FULL_SEARCH:
{
// dead slow exhaustive search, but at least it uses sad_x4()
......
}
break;
}
default:
X265_CHECK(0, "invalid motion estimate mode\n");
break;
}
if (bprecost < bcost)
{
bmv = bestpre;
bcost = bprecost;
}
else
bmv = bmv.toQPel(); // promote search bmv to qpel
const SubpelWorkload& wl = workload[this->subpelRefine];
// check mv range for slice bound
if ((maxSlices > 1) & ((bmv.y < qmvmin.y) | (bmv.y > qmvmax.y)))
{
bmv.y = x265_min(x265_max(bmv.y, qmvmin.y), qmvmax.y);
bcost = subpelCompare(ref, bmv, satd) + mvcost(bmv);
}
if (!bcost)
{
/* if there was zero residual at the clipped MVP, we can skip subpel
* refine, but we do need to include the mvcost in the returned cost */
bcost = mvcost(bmv);
}
else if (ref->isLowres)
{
int bdir = 0;
for (int i = 1; i <= wl.hpel_dirs; i++)
{
MV qmv = bmv + square1[i] * 2;
/* skip invalid range */
if ((qmv.y < qmvmin.y) | (qmv.y > qmvmax.y))
continue;
int cost = ref->lowresQPelCost(fenc, blockOffset, qmv, sad, hme) + mvcost(qmv);
COPY2_IF_LT(bcost, cost, bdir, i);
}
bmv += square1[bdir] * 2;
bcost = ref->lowresQPelCost(fenc, blockOffset, bmv, satd, hme) + mvcost(bmv);
bdir = 0;
for (int i = 1; i <= wl.qpel_dirs; i++)
{
MV qmv = bmv + square1[i];
/* skip invalid range */
if ((qmv.y < qmvmin.y) | (qmv.y > qmvmax.y))
continue;
int cost = ref->lowresQPelCost(fenc, blockOffset, qmv, satd, hme) + mvcost(qmv);
COPY2_IF_LT(bcost, cost, bdir, i);
}
bmv += square1[bdir];
}
else
{
pixelcmp_t hpelcomp;
if (wl.hpel_satd)
{
bcost = subpelCompare(ref, bmv, satd) + mvcost(bmv);
hpelcomp = satd;
}
else
hpelcomp = sad;
for (int iter = 0; iter < wl.hpel_iters; iter++)
{
int bdir = 0;
for (int i = 1; i <= wl.hpel_dirs; i++)
{
MV qmv = bmv + square1[i] * 2;
// check mv range for slice bound
if ((qmv.y < qmvmin.y) | (qmv.y > qmvmax.y))
continue;
int cost = subpelCompare(ref, qmv, hpelcomp) + mvcost(qmv);
COPY2_IF_LT(bcost, cost, bdir, i);
}
if (bdir)
bmv += square1[bdir] * 2;
else
break;
}
/* if HPEL search used SAD, remeasure with SATD before QPEL */
if (!wl.hpel_satd)
bcost = subpelCompare(ref, bmv, satd) + mvcost(bmv);
for (int iter = 0; iter < wl.qpel_iters; iter++)
{
int bdir = 0;
for (int i = 1; i <= wl.qpel_dirs; i++)
{
MV qmv = bmv + square1[i];
// check mv range for slice bound
if ((qmv.y < qmvmin.y) | (qmv.y > qmvmax.y))
continue;
int cost = subpelCompare(ref, qmv, satd) + mvcost(qmv);
COPY2_IF_LT(bcost, cost, bdir, i);
}
if (bdir)
bmv += square1[bdir];
else
break;
}
}
// check mv range for slice bound
X265_CHECK(((bmv.y >= qmvmin.y) & (bmv.y <= qmvmax.y)), "mv beyond range!");
x265_emms();
outQMv = bmv;
return bcost;
}
将运动估计得到的MV加到参考块上得到预测块,用于后续计算残差和编码
void Predict::motionCompensation(const CUData& cu, const PredictionUnit& pu, Yuv& predYuv, bool bLuma, bool bChroma)
{
int refIdx0 = cu.m_refIdx[0][pu.puAbsPartIdx];
int refIdx1 = cu.m_refIdx[1][pu.puAbsPartIdx];
if (cu.m_slice->isInterP())
{
/* P Slice */
WeightValues wv0[3];
X265_CHECK(refIdx0 >= 0, "invalid P refidx\n");
X265_CHECK(refIdx0 < cu.m_slice->m_numRefIdx[0], "P refidx out of range\n");
const WeightParam *wp0 = cu.m_slice->m_weightPredTable[0][refIdx0];
MV mv0 = cu.m_mv[0][pu.puAbsPartIdx];
cu.clipMv(mv0);
if (cu.m_slice->m_pps->bUseWeightPred && wp0->wtPresent)//加权预测
{
for (int plane = 0; plane < (bChroma ? 3 : 1); plane++)
{
wv0[plane].w = wp0[plane].inputWeight;
wv0[plane].offset = wp0[plane].inputOffset * (1 << (X265_DEPTH - 8));
wv0[plane].shift = wp0[plane].log2WeightDenom;
wv0[plane].round = wp0[plane].log2WeightDenom >= 1 ? 1 << (wp0[plane].log2WeightDenom - 1) : 0;
}
ShortYuv& shortYuv = m_predShortYuv[0];//为了避免浮点运算,需要放大加权系数,因此需要使用short型变量(16位)
if (bLuma)
predInterLumaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
if (bChroma)
predInterChromaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
addWeightUni(pu, predYuv, shortYuv, wv0, bLuma, bChroma);
}
else
{
if (bLuma)
predInterLumaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
if (bChroma)
predInterChromaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
}
}
else
{
/* B Slice */
WeightValues wv0[3], wv1[3];
const WeightParam *pwp0, *pwp1;
X265_CHECK(refIdx0 < cu.m_slice->m_numRefIdx[0], "bidir refidx0 out of range\n");
X265_CHECK(refIdx1 < cu.m_slice->m_numRefIdx[1], "bidir refidx1 out of range\n");
if (cu.m_slice->m_pps->bUseWeightedBiPred)
{
pwp0 = refIdx0 >= 0 ? cu.m_slice->m_weightPredTable[0][refIdx0] : NULL;
pwp1 = refIdx1 >= 0 ? cu.m_slice->m_weightPredTable[1][refIdx1] : NULL;
if (pwp0 && pwp1 && (pwp0->wtPresent || pwp1->wtPresent))
{//B帧有可能有两个参考图像队列,这两个队列是否使用加权预测需要单独处理
/* biprediction weighting */
for (int plane = 0; plane < (bChroma ? 3 : 1); plane++)
{
wv0[plane].w = pwp0[plane].inputWeight;
wv0[plane].o = pwp0[plane].inputOffset * (1 << (X265_DEPTH - 8));
wv0[plane].shift = pwp0[plane].log2WeightDenom;
wv0[plane].round = 1 << pwp0[plane].log2WeightDenom;
wv1[plane].w = pwp1[plane].inputWeight;
wv1[plane].o = pwp1[plane].inputOffset * (1 << (X265_DEPTH - 8));
wv1[plane].shift = wv0[plane].shift;
wv1[plane].round = wv0[plane].round;
}
}
else
{
/* uniprediction weighting, always outputs to wv0 */
const WeightParam* pwp = (refIdx0 >= 0) ? pwp0 : pwp1;
for (int plane = 0; plane < (bChroma ? 3 : 1); plane++)
{
wv0[plane].w = pwp[plane].inputWeight;
wv0[plane].offset = pwp[plane].inputOffset * (1 << (X265_DEPTH - 8));
wv0[plane].shift = pwp[plane].log2WeightDenom;
wv0[plane].round = pwp[plane].log2WeightDenom >= 1 ? 1 << (pwp[plane].log2WeightDenom - 1) : 0;
}
}
}
else
pwp0 = pwp1 = NULL;
if (refIdx0 >= 0 && refIdx1 >= 0)
{
MV mv0 = cu.m_mv[0][pu.puAbsPartIdx];
MV mv1 = cu.m_mv[1][pu.puAbsPartIdx];
cu.clipMv(mv0);
cu.clipMv(mv1);
if (bLuma)
{
predInterLumaShort(pu, m_predShortYuv[0], *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
predInterLumaShort(pu, m_predShortYuv[1], *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
}
if (bChroma)
{
predInterChromaShort(pu, m_predShortYuv[0], *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
predInterChromaShort(pu, m_predShortYuv[1], *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
}
if (pwp0 && pwp1 && (pwp0->wtPresent || pwp1->wtPresent))
addWeightBi(pu, predYuv, m_predShortYuv[0], m_predShortYuv[1], wv0, wv1, bLuma, bChroma);
else
predYuv.addAvg(m_predShortYuv[0], m_predShortYuv[1], pu.puAbsPartIdx, pu.width, pu.height, bLuma, bChroma);
}
else if (refIdx0 >= 0)
{
MV mv0 = cu.m_mv[0][pu.puAbsPartIdx];
cu.clipMv(mv0);
if (pwp0 && pwp0->wtPresent)
{
ShortYuv& shortYuv = m_predShortYuv[0];
if (bLuma)
predInterLumaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
if (bChroma)
predInterChromaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
addWeightUni(pu, predYuv, shortYuv, wv0, bLuma, bChroma);
}
else
{
if (bLuma)
predInterLumaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
if (bChroma)
predInterChromaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[0][refIdx0], mv0);
}
}
else
{
MV mv1 = cu.m_mv[1][pu.puAbsPartIdx];
cu.clipMv(mv1);
/* uniprediction to L1 */
X265_CHECK(refIdx1 >= 0, "refidx1 was not positive\n");
if (pwp1 && pwp1->wtPresent)
{
ShortYuv& shortYuv = m_predShortYuv[0];
if (bLuma)
predInterLumaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
if (bChroma)
predInterChromaShort(pu, shortYuv, *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
addWeightUni(pu, predYuv, shortYuv, wv0, bLuma, bChroma);
}
else
{
if (bLuma)
predInterLumaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
if (bChroma)
predInterChromaPixel(pu, predYuv, *cu.m_slice->m_refReconPicList[1][refIdx1], mv1);
}
}
}
}