package org.jcodec.codecs.h264.decode;
import static org.jcodec.codecs.h264.H264Const.COMP_BLOCK_4x4_LUT;
import static org.jcodec.codecs.h264.H264Const.COMP_BLOCK_8x8_LUT;
import static org.jcodec.codecs.h264.H264Const.COMP_POS_4x4_LUT;
import static org.jcodec.codecs.h264.H264Const.COMP_POS_8x8_LUT;
import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.NULL_VECTOR;
import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.calcMVPredictionMedian;
import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.collectPredictors;
import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.copyVect;
import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.debugPrint;
import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.mergeResidual;
import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.saveMvs;
import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.saveVect;
import static org.jcodec.codecs.h264.decode.PredictionMerger.mergePrediction;
import static org.jcodec.common.model.ColorSpace.MONO;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Const.PartPred;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.Picture8Bit;
/**
* A decoder for Inter 16x16, 16x8 and 8x16 macroblocks
*
* @author The JCodec project
*/
public class MBlockDecoderInter extends MBlockDecoderBase {
private Mapper mapper;
public MBlockDecoderInter(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
}
public void decode16x16(MBlock mBlock, Picture8Bit mb, Frame[][] refs, PartPred p0) {
int mbX = mapper.getMbX(mBlock.mbIdx);
int mbY = mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = mapper.topRightAvailable(mBlock.mbIdx);
int address = mapper.getAddress(mBlock.mbIdx);
int[][][] x = new int[2][][];
int xx = mbX << 2;
for (int list = 0; list < 2; list++) {
predictInter16x16(mBlock, mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable,
topRightAvailable, x, xx, list, p0);
}
PredictionMerger.mergePrediction(sh, x[0][0][2], x[1][0][2], p0, 0, mbb[0].getPlaneData(0),
mbb[1].getPlaneData(0), 0, 16, 16, 16, mb.getPlaneData(0), refs, poc);
PartPred[] partPreds = new PartPred[] { p0, p0, p0, p0 };
predictChromaInter(refs, x, mbX << 3, mbY << 3, 1, mb, partPreds);
predictChromaInter(refs, x, mbX << 3, mbY << 3, 2, mb, partPreds);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, x, partPreds,
mapper.getAddress(mBlock.mbIdx));
mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? COMP_BLOCK_8x8_LUT : COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? COMP_POS_8x8_LUT : COMP_POS_4x4_LUT);
collectPredictors(s, mb, mbX);
di.mbTypes[address] = mBlock.curMbType;
}
private void predictInter8x16(MBlock mBlock, Picture8Bit mb, Picture8Bit[][] references, int mbX, int mbY,
boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, int[][][] x,
int list, PartPred p0, PartPred p1) {
int xx = mbX << 2;
int mvX1 = 0, mvY1 = 0, r1 = -1, mvX2 = 0, mvY2 = 0, r2 = -1;
if (H264Const.usesList(p0, list)) {
int mvpX1 = calcMVPrediction8x16Left(s.mvLeft[list][0], s.mvTop[list][mbX << 2],
s.mvTop[list][(mbX << 2) + 2], s.mvTopLeft[list], leftAvailable, topAvailable, topAvailable,
tlAvailable, mBlock.pb168x168.refIdx1[list], 0);
int mvpY1 = calcMVPrediction8x16Left(s.mvLeft[list][0], s.mvTop[list][mbX << 2],
s.mvTop[list][(mbX << 2) + 2], s.mvTopLeft[list], leftAvailable, topAvailable, topAvailable,
tlAvailable, mBlock.pb168x168.refIdx1[list], 1);
mvX1 = mBlock.pb168x168.mvdX1[list] + mvpX1;
mvY1 = mBlock.pb168x168.mvdY1[list] + mvpY1;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb168x168.mvdX1[list],
mBlock.pb168x168.mvdY1[list], mvX1, mvY1, mBlock.pb168x168.refIdx1[list]);
interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx1[list]], mb, 0, (mbX << 6) + mvX1,
(mbY << 6) + mvY1, 8, 16);
r1 = mBlock.pb168x168.refIdx1[list];
}
int[] v1 = { mvX1, mvY1, r1 };
if (H264Const.usesList(p1, list)) {
int mvpX2 = calcMVPrediction8x16Right(v1, s.mvTop[list][(mbX << 2) + 2], s.mvTop[list][(mbX << 2) + 4],
s.mvTop[list][(mbX << 2) + 1], true, topAvailable, trAvailable, topAvailable,
mBlock.pb168x168.refIdx2[list], 0);
int mvpY2 = calcMVPrediction8x16Right(v1, s.mvTop[list][(mbX << 2) + 2], s.mvTop[list][(mbX << 2) + 4],
s.mvTop[list][(mbX << 2) + 1], true, topAvailable, trAvailable, topAvailable,
mBlock.pb168x168.refIdx2[list], 1);
mvX2 = mBlock.pb168x168.mvdX2[list] + mvpX2;
mvY2 = mBlock.pb168x168.mvdY2[list] + mvpY2;
debugPrint("MVP: (" + mvpX2 + ", " + mvpY2 + "), MVD: (" + mBlock.pb168x168.mvdX2[list] + ", "
+ mBlock.pb168x168.mvdY2[list] + "), MV: (" + mvX2 + "," + mvY2 + ","
+ mBlock.pb168x168.refIdx2[list] + ")");
interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx2[list]], mb, 8, (mbX << 6) + 32
+ mvX2, (mbY << 6) + mvY2, 8, 16);
r2 = mBlock.pb168x168.refIdx2[list];
}
int[] v2 = { mvX2, mvY2, r2 };
copyVect(s.mvTopLeft[list], s.mvTop[list][xx + 3]);
saveVect(s.mvTop[list], xx, xx + 2, mvX1, mvY1, r1);
saveVect(s.mvTop[list], xx + 2, xx + 4, mvX2, mvY2, r2);
saveVect(s.mvLeft[list], 0, 4, mvX2, mvY2, r2);
x[list] = new int[][] { v1, v1, v2, v2, v1, v1, v2, v2, v1, v1, v2, v2, v1, v1, v2, v2 };
}
private void predictInter16x8(MBlock mBlock, Picture8Bit mb, Picture8Bit[][] references, int mbX, int mbY,
boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, int xx, int[][][] x,
PartPred p0, PartPred p1, int list) {
int mvX1 = 0, mvY1 = 0, mvX2 = 0, mvY2 = 0, r1 = -1, r2 = -1;
if (H264Const.usesList(p0, list)) {
int mvpX1 = calcMVPrediction16x8Top(s.mvLeft[list][0], s.mvTop[list][mbX << 2],
s.mvTop[list][(mbX << 2) + 4], s.mvTopLeft[list], leftAvailable, topAvailable, trAvailable,
tlAvailable, mBlock.pb168x168.refIdx1[list], 0);
int mvpY1 = calcMVPrediction16x8Top(s.mvLeft[list][0], s.mvTop[list][mbX << 2],
s.mvTop[list][(mbX << 2) + 4], s.mvTopLeft[list], leftAvailable, topAvailable, trAvailable,
tlAvailable, mBlock.pb168x168.refIdx1[list], 1);
mvX1 = mBlock.pb168x168.mvdX1[list] + mvpX1;
mvY1 = mBlock.pb168x168.mvdY1[list] + mvpY1;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb168x168.mvdX1[list],
mBlock.pb168x168.mvdY1[list], mvX1, mvY1, mBlock.pb168x168.refIdx1[list]);
interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx1[list]], mb, 0, (mbX << 6) + mvX1,
(mbY << 6) + mvY1, 16, 8);
r1 = mBlock.pb168x168.refIdx1[list];
}
int[] v1 = { mvX1, mvY1, r1 };
if (H264Const.usesList(p1, list)) {
int mvpX2 = calcMVPrediction16x8Bottom(s.mvLeft[list][2], v1, null, s.mvLeft[list][1], leftAvailable, true,
false, leftAvailable, mBlock.pb168x168.refIdx2[list], 0);
int mvpY2 = calcMVPrediction16x8Bottom(s.mvLeft[list][2], v1, null, s.mvLeft[list][1], leftAvailable, true,
false, leftAvailable, mBlock.pb168x168.refIdx2[list], 1);
mvX2 = mBlock.pb168x168.mvdX2[list] + mvpX2;
mvY2 = mBlock.pb168x168.mvdY2[list] + mvpY2;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb168x168.mvdX2[list],
mBlock.pb168x168.mvdY2[list], mvX2, mvY2, mBlock.pb168x168.refIdx2[list]);
interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx2[list]], mb, 128,
(mbX << 6) + mvX2, (mbY << 6) + 32 + mvY2, 16, 8);
r2 = mBlock.pb168x168.refIdx2[list];
}
int[] v2 = { mvX2, mvY2, r2 };
copyVect(s.mvTopLeft[list], s.mvTop[list][xx + 3]);
saveVect(s.mvLeft[list], 0, 2, mvX1, mvY1, r1);
saveVect(s.mvLeft[list], 2, 4, mvX2, mvY2, r2);
saveVect(s.mvTop[list], xx, xx + 4, mvX2, mvY2, r2);
x[list] = new int[][] { v1, v1, v1, v1, v1, v1, v1, v1, v2, v2, v2, v2, v2, v2, v2, v2 };
}
public void decode16x8(MBlock mBlock, Picture8Bit mb, Frame[][] refs, PartPred p0, PartPred p1) {
int mbX = mapper.getMbX(mBlock.mbIdx);
int mbY = mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = mapper.topRightAvailable(mBlock.mbIdx);
int address = mapper.getAddress(mBlock.mbIdx);
int[][][] x = new int[2][][];
int xx = mbX << 2;
for (int list = 0; list < 2; list++) {
predictInter16x8(mBlock, mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable,
topRightAvailable, xx, x, p0, p1, list);
}
mergePrediction(sh, x[0][0][2], x[1][0][2], p0, 0, mbb[0].getPlaneData(0), mbb[1].getPlaneData(0), 0, 16, 16,
8, mb.getPlaneData(0), refs, poc);
mergePrediction(sh, x[0][8][2], x[1][8][2], p1, 0, mbb[0].getPlaneData(0), mbb[1].getPlaneData(0), 128, 16, 16,
8, mb.getPlaneData(0), refs, poc);
PartPred[] partPreds = new PartPred[] { p0, p0, p1, p1 };
predictChromaInter(refs, x, mbX << 3, mbY << 3, 1, mb, partPreds);
predictChromaInter(refs, x, mbX << 3, mbY << 3, 2, mb, partPreds);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, x, partPreds,
mapper.getAddress(mBlock.mbIdx));
mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? COMP_BLOCK_8x8_LUT : COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? COMP_POS_8x8_LUT : COMP_POS_4x4_LUT);
collectPredictors(s, mb, mbX);
di.mbTypes[address] = mBlock.curMbType;
}
public void decode8x16(MBlock mBlock, Picture8Bit mb, Frame[][] refs, PartPred p0, PartPred p1) {
int mbX = mapper.getMbX(mBlock.mbIdx);
int mbY = mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = mapper.topRightAvailable(mBlock.mbIdx);
int address = mapper.getAddress(mBlock.mbIdx);
int[][][] x = new int[2][][];
for (int list = 0; list < 2; list++) {
predictInter8x16(mBlock, mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable,
topRightAvailable, x, list, p0, p1);
}
mergePrediction(sh, x[0][0][2], x[1][0][2], p0, 0, mbb[0].getPlaneData(0), mbb[1].getPlaneData(0), 0, 16, 8,
16, mb.getPlaneData(0), refs, poc);
mergePrediction(sh, x[0][2][2], x[1][2][2], p1, 0, mbb[0].getPlaneData(0), mbb[1].getPlaneData(0), 8, 16, 8,
16, mb.getPlaneData(0), refs, poc);
PartPred[] predType = new PartPred[] { p0, p1, p0, p1 };
predictChromaInter(refs, x, mbX << 3, mbY << 3, 1, mb, predType);
predictChromaInter(refs, x, mbX << 3, mbY << 3, 2, mb, predType);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, x, predType, mapper.getAddress(mBlock.mbIdx));
mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? COMP_BLOCK_8x8_LUT : COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? COMP_POS_8x8_LUT : COMP_POS_4x4_LUT);
collectPredictors(s, mb, mbX);
di.mbTypes[address] = mBlock.curMbType;
}
void predictInter16x16(MBlock mBlock, Picture8Bit mb, Picture8Bit[][] references, int mbX, int mbY,
boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, int[][][] x, int xx,
int list, PartPred curPred) {
int mvX = 0, mvY = 0, r = -1;
if (H264Const.usesList(curPred, list)) {
int mvpX = calcMVPredictionMedian(s.mvLeft[list][0], s.mvTop[list][mbX << 2],
s.mvTop[list][(mbX << 2) + 4], s.mvTopLeft[list], leftAvailable, topAvailable, trAvailable,
tlAvailable, mBlock.pb16x16.refIdx[list], 0);
int mvpY = calcMVPredictionMedian(s.mvLeft[list][0], s.mvTop[list][mbX << 2],
s.mvTop[list][(mbX << 2) + 4], s.mvTopLeft[list], leftAvailable, topAvailable, trAvailable,
tlAvailable, mBlock.pb16x16.refIdx[list], 1);
mvX = mBlock.pb16x16.mvdX[list] + mvpX;
mvY = mBlock.pb16x16.mvdY[list] + mvpY;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX, mvpY, mBlock.pb16x16.mvdX[list],
mBlock.pb16x16.mvdY[list], mvX, mvY, mBlock.pb16x16.refIdx[list]);
r = mBlock.pb16x16.refIdx[list];
interpolator.getBlockLuma(references[list][r], mb, 0, (mbX << 6) + mvX, (mbY << 6) + mvY, 16, 16);
}
copyVect(s.mvTopLeft[list], s.mvTop[list][xx + 3]);
saveVect(s.mvTop[list], xx, xx + 4, mvX, mvY, r);
saveVect(s.mvLeft[list], 0, 4, mvX, mvY, r);
int[] v = { mvX, mvY, r };
x[list] = new int[][] { v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v };
}
private void residualInter(MBlock mBlock, Frame[][] refs, boolean leftAvailable, boolean topAvailable, int mbX,
int mbY, int[][][] x, PartPred[] pp, int mbAddr) {
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0) {
s.qp = (s.qp + mBlock.mbQPDelta + 52) % 52;
}
di.mbQps[0][mbAddr] = s.qp;
residualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
saveMvs(di, x, mbX, mbY);
if (s.chromaFormat != MONO) {
int qp1 = calcQpChroma(s.qp, s.chromaQpOffset[0]);
int qp2 = calcQpChroma(s.qp, s.chromaQpOffset[1]);
decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, qp1, qp2);
di.mbQps[1][mbAddr] = qp1;
di.mbQps[2][mbAddr] = qp2;
}
di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
}
public int calcMVPrediction16x8Top(int[] a, int[] b, int[] c, int[] d, boolean aAvb, boolean bAvb, boolean cAvb,
boolean dAvb, int refIdx, int comp) {
if (bAvb && b[2] == refIdx)
return b[comp];
else
return calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction16x8Bottom(int[] a, int[] b, int[] c, int[] d, boolean aAvb, boolean bAvb, boolean cAvb,
boolean dAvb, int refIdx, int comp) {
if (aAvb && a[2] == refIdx)
return a[comp];
else
return calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction8x16Left(int[] a, int[] b, int[] c, int[] d, boolean aAvb, boolean bAvb, boolean cAvb,
boolean dAvb, int refIdx, int comp) {
if (aAvb && a[2] == refIdx)
return a[comp];
else
return calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction8x16Right(int[] a, int[] b, int[] c, int[] d, boolean aAvb, boolean bAvb, boolean cAvb,
boolean dAvb, int refIdx, int comp) {
int[] lc = cAvb ? c : (dAvb ? d : NULL_VECTOR);
if (lc[2] == refIdx)
return lc[comp];
else
return calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
}