package org.jcodec.codecs.h264.decode;
import static org.jcodec.codecs.h264.H264Const.ARRAY;
import static org.jcodec.codecs.h264.H264Const.BLK8x8_BLOCKS;
import static org.jcodec.codecs.h264.H264Const.BLK_8x8_MB_OFF_LUMA;
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.H264Const.bPartPredModes;
import static org.jcodec.codecs.h264.H264Const.bSubMbTypes;
import static org.jcodec.codecs.h264.H264Const.PartPred.Direct;
import static org.jcodec.codecs.h264.H264Const.PartPred.L0;
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.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.savePrediction8x8;
import static org.jcodec.codecs.h264.decode.PredictionMerger.mergePrediction;
import static org.jcodec.codecs.h264.decode.PredictionMerger.weightPrediction;
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.codecs.h264.io.model.SliceType;
import org.jcodec.common.model.Picture8Bit;
import java.util.Arrays;
/**
* A decoder for Inter 16x16, 16x8 and 8x16 macroblocks
*
* @author The JCodec project
*/
public class MBlockDecoderInter8x8 extends MBlockDecoderBase {
private Mapper mapper;
private MBlockDecoderBDirect bDirectDecoder;
public MBlockDecoderInter8x8(Mapper mapper, MBlockDecoderBDirect bDirectDecoder, SliceHeader sh, DeblockerInput di,
int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
this.bDirectDecoder = bDirectDecoder;
}
public void decode(MBlock mBlock, Frame[][] references, Picture8Bit mb, SliceType sliceType, boolean ref0) {
int mbX = mapper.getMbX(mBlock.mbIdx);
int mbY = mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = mapper.topAvailable(mBlock.mbIdx);
int mbAddr = mapper.getAddress(mBlock.mbIdx);
boolean topLeftAvailable = mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = mapper.topRightAvailable(mBlock.mbIdx);
int[][][] x = new int[2][16][3];
PartPred[] pp = new PartPred[4];
for (int i = 0; i < 16; i++)
x[0][i][2] = x[1][i][2] = -1;
if (sliceType == SliceType.P) {
predict8x8P(mBlock, references[0], mb, ref0, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable,
topRightAvailable, x, pp);
} else {
predict8x8B(mBlock, references, mb, ref0, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable,
topRightAvailable, x, pp);
}
predictChromaInter(references, x, mbX << 3, mbY << 3, 1, mb, pp);
predictChromaInter(references, x, mbX << 3, mbY << 3, 2, mb, pp);
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);
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;
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[mbAddr] = mBlock.curMbType;
di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
}
private void predict8x8P(MBlock mBlock, Picture8Bit[] references, Picture8Bit mb, boolean ref0, int mbX, int mbY,
boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean topRightAvailable, int[][][] x,
PartPred[] pp) {
decodeSubMb8x8(mBlock, 0, mBlock.pb8x8.subMbTypes[0], references, mbX << 6, mbY << 6, x[0], s.mvTopLeft[0],
s.mvTop[0][mbX << 2], s.mvTop[0][(mbX << 2) + 1], s.mvTop[0][(mbX << 2) + 2], s.mvLeft[0][0],
s.mvLeft[0][1], tlAvailable, topAvailable, topAvailable, leftAvailable, x[0][0], x[0][1], x[0][4],
x[0][5], mBlock.pb8x8.refIdx[0][0], mb, 0, 0);
decodeSubMb8x8(mBlock, 1, mBlock.pb8x8.subMbTypes[1], references, (mbX << 6) + 32, mbY << 6, x[0],
s.mvTop[0][(mbX << 2) + 1], s.mvTop[0][(mbX << 2) + 2], s.mvTop[0][(mbX << 2) + 3],
s.mvTop[0][(mbX << 2) + 4], x[0][1], x[0][5], topAvailable, topAvailable, topRightAvailable, true,
x[0][2], x[0][3], x[0][6], x[0][7], mBlock.pb8x8.refIdx[0][1], mb, 8, 0);
decodeSubMb8x8(mBlock, 2, mBlock.pb8x8.subMbTypes[2], references, mbX << 6, (mbY << 6) + 32, x[0],
s.mvLeft[0][1], x[0][4], x[0][5], x[0][6], s.mvLeft[0][2], s.mvLeft[0][3], leftAvailable, true, true,
leftAvailable, x[0][8], x[0][9], x[0][12], x[0][13], mBlock.pb8x8.refIdx[0][2], mb, 128, 0);
decodeSubMb8x8(mBlock, 3, mBlock.pb8x8.subMbTypes[3], references, (mbX << 6) + 32, (mbY << 6) + 32, x[0],
x[0][5], x[0][6], x[0][7], null, x[0][9], x[0][13], true, true, false, true, x[0][10], x[0][11],
x[0][14], x[0][15], mBlock.pb8x8.refIdx[0][3], mb, 136, 0);
for (int i = 0; i < 4; i++) {
// TODO(stan): refactor this
int blk4x4 = BLK8x8_BLOCKS[i][0];
weightPrediction(sh, x[0][blk4x4][2], 0, mb.getPlaneData(0), BLK_8x8_MB_OFF_LUMA[i], 16, 8,
8, mb.getPlaneData(0));
}
savePrediction8x8(s, mbX, x[0], 0);
Arrays.fill(pp, L0);
}
private void predict8x8B(MBlock mBlock, Frame[][] refs, Picture8Bit mb, boolean ref0, int mbX, int mbY,
boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean topRightAvailable, int[][][] x,
PartPred[] p) {
for (int i = 0; i < 4; i++) {
p[i] = bPartPredModes[mBlock.pb8x8.subMbTypes[i]];
}
int blk8x8X = mbX << 1;
PartPred[] _pp = new PartPred[4];
for (int i = 0; i < 4; i++) {
if (p[i] == Direct)
bDirectDecoder.predictBDirect(refs, mbX, mbY, leftAvailable, topAvailable, tlAvailable,
topRightAvailable, x, _pp, mb, ARRAY[i]);
}
for (int list = 0; list < 2; list++) {
if (H264Const.usesList(p[0], list)) {
decodeSubMb8x8(mBlock, 0, bSubMbTypes[mBlock.pb8x8.subMbTypes[0]], refs[list], mbX << 6, mbY << 6,
x[list], s.mvTopLeft[list], s.mvTop[list][mbX << 2], s.mvTop[list][(mbX << 2) + 1],
s.mvTop[list][(mbX << 2) + 2], s.mvLeft[list][0], s.mvLeft[list][1], tlAvailable, topAvailable,
topAvailable, leftAvailable, x[list][0], x[list][1], x[list][4], x[list][5],
mBlock.pb8x8.refIdx[list][0], mbb[list], 0, list);
}
if (H264Const.usesList(p[1], list)) {
decodeSubMb8x8(mBlock, 1, bSubMbTypes[mBlock.pb8x8.subMbTypes[1]], refs[list], (mbX << 6) + 32,
mbY << 6, x[list], s.mvTop[list][(mbX << 2) + 1], s.mvTop[list][(mbX << 2) + 2],
s.mvTop[list][(mbX << 2) + 3], s.mvTop[list][(mbX << 2) + 4], x[list][1], x[list][5],
topAvailable, topAvailable, topRightAvailable, true, x[list][2], x[list][3], x[list][6],
x[list][7], mBlock.pb8x8.refIdx[list][1], mbb[list], 8, list);
}
if (H264Const.usesList(p[2], list)) {
decodeSubMb8x8(mBlock, 2, bSubMbTypes[mBlock.pb8x8.subMbTypes[2]], refs[list], mbX << 6,
(mbY << 6) + 32, x[list], s.mvLeft[list][1], x[list][4], x[list][5], x[list][6],
s.mvLeft[list][2], s.mvLeft[list][3], leftAvailable, true, true, leftAvailable, x[list][8],
x[list][9], x[list][12], x[list][13], mBlock.pb8x8.refIdx[list][2], mbb[list], 128, list);
}
if (H264Const.usesList(p[3], list)) {
decodeSubMb8x8(mBlock, 3, bSubMbTypes[mBlock.pb8x8.subMbTypes[3]], refs[list], (mbX << 6) + 32,
(mbY << 6) + 32, x[list], x[list][5], x[list][6], x[list][7], null, x[list][9], x[list][13],
true, true, false, true, x[list][10], x[list][11], x[list][14], x[list][15],
mBlock.pb8x8.refIdx[list][3], mbb[list], 136, list);
}
}
for (int i = 0; i < 4; i++) {
int blk4x4 = BLK8x8_BLOCKS[i][0];
mergePrediction(sh, x[0][blk4x4][2], x[1][blk4x4][2], p[i], 0, mbb[0].getPlaneData(0),
mbb[1].getPlaneData(0), BLK_8x8_MB_OFF_LUMA[i], 16, 8, 8, mb.getPlaneData(0), refs, poc);
}
savePrediction8x8(s, mbX, x[0], 0);
savePrediction8x8(s, mbX, x[1], 1);
for (int i = 0; i < 4; i++)
if (p[i] == Direct)
p[i] = _pp[i];
}
private void decodeSubMb8x8(MBlock mBlock, int partNo, int subMbType, Picture8Bit[] references, int offX, int offY,
int[][] x, int[] tl, int[] t0, int[] t1, int[] tr, int[] l0, int[] l1, boolean tlAvb, boolean tAvb,
boolean trAvb, boolean lAvb, int[] x00, int[] x01, int[] x10, int[] x11, int refIdx, Picture8Bit mb,
int off, int list) {
x00[2] = x01[2] = x10[2] = x11[2] = refIdx;
switch (subMbType) {
case 3:
decodeSub4x4(mBlock, partNo, references, offX, offY, tl, t0, t1, tr, l0, l1, tlAvb, tAvb, trAvb, lAvb, x00,
x01, x10, x11, refIdx, mb, off, list);
break;
case 2:
decodeSub4x8(mBlock, partNo, references, offX, offY, tl, t0, t1, tr, l0, tlAvb, tAvb, trAvb, lAvb, x00,
x01, x10, x11, refIdx, mb, off, list);
break;
case 1:
decodeSub8x4(mBlock, partNo, references, offX, offY, tl, t0, tr, l0, l1, tlAvb, tAvb, trAvb, lAvb, x00,
x01, x10, x11, refIdx, mb, off, list);
break;
case 0:
decodeSub8x8(mBlock, partNo, references, offX, offY, tl, t0, tr, l0, tlAvb, tAvb, trAvb, lAvb, x00, x01,
x10, x11, refIdx, mb, off, list);
}
}
private void decodeSub8x8(MBlock mBlock, int partNo, Picture8Bit[] references, int offX, int offY, int[] tl,
int[] t0, int[] tr, int[] l0, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, int[] x00,
int[] x01, int[] x10, int[] x11, int refIdx, Picture8Bit mb, int off, int list) {
int mvpX = calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 0);
int mvpY = calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 1);
x00[0] = x01[0] = x10[0] = x11[0] = mBlock.pb8x8.mvdX1[list][partNo] + mvpX;
x00[1] = x01[1] = x10[1] = x11[1] = mBlock.pb8x8.mvdY1[list][partNo] + mvpY;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX, mvpY, mBlock.pb8x8.mvdX1[list][partNo],
mBlock.pb8x8.mvdY1[list][partNo], x00[0], x00[1], refIdx);
interpolator.getBlockLuma(references[refIdx], mb, off, offX + x00[0], offY + x00[1], 8, 8);
}
private void decodeSub8x4(MBlock mBlock, int partNo, Picture8Bit[] references, int offX, int offY, int[] tl,
int[] t0, int[] tr, int[] l0, int[] l1, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb,
int[] x00, int[] x01, int[] x10, int[] x11, int refIdx, Picture8Bit mb, int off, int list) {
int mvpX1 = calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 0);
int mvpY1 = calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 1);
x00[0] = x01[0] = mBlock.pb8x8.mvdX1[list][partNo] + mvpX1;
x00[1] = x01[1] = mBlock.pb8x8.mvdY1[list][partNo] + mvpY1;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo],
mBlock.pb8x8.mvdY1[list][partNo], x00[0], x00[1], refIdx);
int mvpX2 = calcMVPredictionMedian(l1, x00, NULL_VECTOR, l0, lAvb, true, false, lAvb, refIdx, 0);
int mvpY2 = calcMVPredictionMedian(l1, x00, NULL_VECTOR, l0, lAvb, true, false, lAvb, refIdx, 1);
x10[0] = x11[0] = mBlock.pb8x8.mvdX2[list][partNo] + mvpX2;
x10[1] = x11[1] = mBlock.pb8x8.mvdY2[list][partNo] + mvpY2;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo],
mBlock.pb8x8.mvdY2[list][partNo], x10[0], x10[1], refIdx);
interpolator.getBlockLuma(references[refIdx], mb, off, offX + x00[0], offY + x00[1], 8, 4);
interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4, offX + x10[0], offY + x10[1]
+ 16, 8, 4);
}
private void decodeSub4x8(MBlock mBlock, int partNo, Picture8Bit[] references, int offX, int offY, int[] tl,
int[] t0, int[] t1, int[] tr, int[] l0, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb,
int[] x00, int[] x01, int[] x10, int[] x11, int refIdx, Picture8Bit mb, int off, int list) {
int mvpX1 = calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 0);
int mvpY1 = calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 1);
x00[0] = x10[0] = mBlock.pb8x8.mvdX1[list][partNo] + mvpX1;
x00[1] = x10[1] = mBlock.pb8x8.mvdY1[list][partNo] + mvpY1;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo],
mBlock.pb8x8.mvdY1[list][partNo], x00[0], x00[1], refIdx);
int mvpX2 = calcMVPredictionMedian(x00, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 0);
int mvpY2 = calcMVPredictionMedian(x00, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 1);
x01[0] = x11[0] = mBlock.pb8x8.mvdX2[list][partNo] + mvpX2;
x01[1] = x11[1] = mBlock.pb8x8.mvdY2[list][partNo] + mvpY2;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo],
mBlock.pb8x8.mvdY2[list][partNo], x01[0], x01[1], refIdx);
interpolator.getBlockLuma(references[refIdx], mb, off, offX + x00[0], offY + x00[1], 4, 8);
interpolator.getBlockLuma(references[refIdx], mb, off + 4, offX + x01[0] + 16, offY + x01[1], 4, 8);
}
private void decodeSub4x4(MBlock mBlock, int partNo, Picture8Bit[] references, int offX, int offY, int[] tl,
int[] t0, int[] t1, int[] tr, int[] l0, int[] l1, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb,
int[] x00, int[] x01, int[] x10, int[] x11, int refIdx, Picture8Bit mb, int off, int list) {
int mvpX1 = calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 0);
int mvpY1 = calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 1);
x00[0] = mBlock.pb8x8.mvdX1[list][partNo] + mvpX1;
x00[1] = mBlock.pb8x8.mvdY1[list][partNo] + mvpY1;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo],
mBlock.pb8x8.mvdY1[list][partNo], x00[0], x00[1], refIdx);
int mvpX2 = calcMVPredictionMedian(x00, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 0);
int mvpY2 = calcMVPredictionMedian(x00, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 1);
x01[0] = mBlock.pb8x8.mvdX2[list][partNo] + mvpX2;
x01[1] = mBlock.pb8x8.mvdY2[list][partNo] + mvpY2;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo],
mBlock.pb8x8.mvdY2[list][partNo], x01[0], x01[1], refIdx);
int mvpX3 = calcMVPredictionMedian(l1, x00, x01, l0, lAvb, true, true, lAvb, refIdx, 0);
int mvpY3 = calcMVPredictionMedian(l1, x00, x01, l0, lAvb, true, true, lAvb, refIdx, 1);
x10[0] = mBlock.pb8x8.mvdX3[list][partNo] + mvpX3;
x10[1] = mBlock.pb8x8.mvdY3[list][partNo] + mvpY3;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX3, mvpY3, mBlock.pb8x8.mvdX3[list][partNo],
mBlock.pb8x8.mvdY3[list][partNo], x10[0], x10[1], refIdx);
int mvpX4 = calcMVPredictionMedian(x10, x01, NULL_VECTOR, x00, true, true, false, true, refIdx, 0);
int mvpY4 = calcMVPredictionMedian(x10, x01, NULL_VECTOR, x00, true, true, false, true, refIdx, 1);
x11[0] = mBlock.pb8x8.mvdX4[list][partNo] + mvpX4;
x11[1] = mBlock.pb8x8.mvdY4[list][partNo] + mvpY4;
debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX4, mvpY4, mBlock.pb8x8.mvdX4[list][partNo],
mBlock.pb8x8.mvdY4[list][partNo], x11[0], x11[1], refIdx);
interpolator.getBlockLuma(references[refIdx], mb, off, offX + x00[0], offY + x00[1], 4, 4);
interpolator.getBlockLuma(references[refIdx], mb, off + 4, offX + x01[0] + 16, offY + x01[1], 4, 4);
interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4, offX + x10[0], offY + x10[1]
+ 16, 4, 4);
interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4 + 4, offX + x11[0] + 16, offY
+ x11[1] + 16, 4, 4);
}
}