package org.jcodec.codecs.h264.decode; import static org.jcodec.codecs.h264.H264Const.BLK8x8_BLOCKS; import static org.jcodec.codecs.h264.H264Const.BLK_8x8_MB_OFF_CHROMA; import static org.jcodec.codecs.h264.H264Const.BLK_INV_MAP; import static org.jcodec.codecs.h264.H264Const.QP_SCALE_CR; 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.io.model.Frame; import org.jcodec.codecs.h264.io.model.MBType; import org.jcodec.codecs.h264.io.model.SliceHeader; import org.jcodec.common.model.Picture8Bit; import org.jcodec.common.tools.MathUtil; import java.util.Arrays; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Base macroblock decoder that contains routines shared by many decoders * * @author The JCodec project * */ public class MBlockDecoderBase { protected DecoderState s; protected SliceHeader sh; protected DeblockerInput di; protected int poc; protected BlockInterpolator interpolator; protected Picture8Bit[] mbb; public MBlockDecoderBase(SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) { this.interpolator = new BlockInterpolator(); this.s = decoderState; this.sh = sh; this.di = di; this.poc = poc; this.mbb = new Picture8Bit[] { Picture8Bit.create(16, 16, s.chromaFormat), Picture8Bit.create(16, 16, s.chromaFormat) }; } void residualLuma(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY) { if (!mBlock.transform8x8Used) { _residualLuma(mBlock); } else if (sh.pps.entropy_coding_mode_flag) { residualLuma8x8CABAC(mBlock); } else { residualLuma8x8CAVLC(mBlock); } } private void _residualLuma(MBlock mBlock) { for (int i = 0; i < 16; i++) { if ((mBlock.cbpLuma() & (1 << (i >> 2))) == 0) { continue; } CoeffTransformer.dequantizeAC(mBlock.ac[0][i], s.qp); CoeffTransformer.idct4x4(mBlock.ac[0][i]); } } private void residualLuma8x8CABAC(MBlock mBlock) { for (int i = 0; i < 4; i++) { if ((mBlock.cbpLuma() & (1 << i)) == 0) { continue; } CoeffTransformer.dequantizeAC8x8(mBlock.ac[0][i], s.qp); CoeffTransformer.idct8x8(mBlock.ac[0][i]); } } private void residualLuma8x8CAVLC(MBlock mBlock) { for (int i = 0; i < 4; i++) { if ((mBlock.cbpLuma() & (1 << i)) == 0) { continue; } CoeffTransformer.dequantizeAC8x8(mBlock.ac[0][i], s.qp); CoeffTransformer.idct8x8(mBlock.ac[0][i]); } } public void decodeChroma(MBlock mBlock, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, Picture8Bit mb, int qp) { if (s.chromaFormat == MONO) { Arrays.fill(mb.getPlaneData(1), (byte) 0); Arrays.fill(mb.getPlaneData(2), (byte) 0); return; } int qp1 = calcQpChroma(qp, s.chromaQpOffset[0]); int qp2 = calcQpChroma(qp, s.chromaQpOffset[1]); if (mBlock.cbpChroma() != 0) { decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, qp1, qp2); } int addr = mbY * (sh.sps.pic_width_in_mbs_minus1 + 1) + mbX; di.mbQps[1][addr] = qp1; di.mbQps[2][addr] = qp2; ChromaPredictionBuilder.predictWithMode(mBlock.ac[1], mBlock.chromaPredictionMode, mbX, leftAvailable, topAvailable, s.leftRow[1], s.topLine[1], s.topLeft[1], mb.getPlaneData(1)); ChromaPredictionBuilder.predictWithMode(mBlock.ac[2], mBlock.chromaPredictionMode, mbX, leftAvailable, topAvailable, s.leftRow[2], s.topLine[2], s.topLeft[2], mb.getPlaneData(2)); } void decodeChromaResidual(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY, int crQp1, int crQp2) { if (mBlock.cbpChroma() != 0) { if ((mBlock.cbpChroma() & 3) > 0) { chromaDC(mbX, leftAvailable, topAvailable, mBlock.dc1, 1, crQp1, mBlock.curMbType); chromaDC(mbX, leftAvailable, topAvailable, mBlock.dc2, 2, crQp2, mBlock.curMbType); } chromaAC(leftAvailable, topAvailable, mbX, mbY, mBlock.dc1, 1, crQp1, mBlock.curMbType, (mBlock.cbpChroma() & 2) > 0, mBlock.ac[1]); chromaAC(leftAvailable, topAvailable, mbX, mbY, mBlock.dc2, 2, crQp2, mBlock.curMbType, (mBlock.cbpChroma() & 2) > 0, mBlock.ac[2]); } } private void chromaDC(int mbX, boolean leftAvailable, boolean topAvailable, int[] dc, int comp, int crQp, MBType curMbType) { CoeffTransformer.invDC2x2(dc); CoeffTransformer.dequantizeDC2x2(dc, crQp); } private void chromaAC(boolean leftAvailable, boolean topAvailable, int mbX, int mbY, int[] dc, int comp, int crQp, MBType curMbType, boolean codedAC, int[][] residualOut) { for (int i = 0; i < dc.length; i++) { int[] ac = residualOut[i]; if (codedAC) { CoeffTransformer.dequantizeAC(ac, crQp); } ac[0] = dc[i]; CoeffTransformer.idct4x4(ac); } } int calcQpChroma(int qp, int crQpOffset) { return QP_SCALE_CR[MathUtil.clip(qp + crQpOffset, 0, 51)]; } // public void decodeChromaInter(MBlock mBlock, MBType curMbType, int // pattern, Frame[][] refs, int[][][] x, // PartPred[] predType, boolean leftAvailable, boolean topAvailable, int // mbX, int mbY, int mbAddr, int qp, // Picture8Bit mb1, int[][] residualCbOut, int[][] residualCrOut) { // // predictChromaInter(refs, x, mbX << 3, mbY << 3, 1, mb1, predType); // predictChromaInter(refs, x, mbX << 3, mbY << 3, 2, mb1, predType); // // parser.readChromaResidual(mBlock, leftAvailable, topAvailable, mbX, // mBlock.cbpChroma(), curMbType); // // int qp1 = calcQpChroma(qp, s.chromaQpOffset[0]); // int qp2 = calcQpChroma(qp, s.chromaQpOffset[1]); // // decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, // pattern, qp1, qp2, curMbType); // // di.mbQps[1][mbAddr] = qp1; // di.mbQps[2][mbAddr] = qp2; // } public void predictChromaInter(Frame[][] refs, int[][][] vectors, int x, int y, int comp, Picture8Bit mb, PartPred[] predType) { for (int blk8x8 = 0; blk8x8 < 4; blk8x8++) { for (int list = 0; list < 2; list++) { if (!H264Const.usesList(predType[blk8x8], list)) continue; for (int blk4x4 = 0; blk4x4 < 4; blk4x4++) { int i = BLK_INV_MAP[(blk8x8 << 2) + blk4x4]; int[] mv = vectors[list][i]; Picture8Bit ref = refs[list][mv[2]]; int blkPox = (i & 3) << 1; int blkPoy = (i >> 2) << 1; int xx = ((x + blkPox) << 3) + mv[0]; int yy = ((y + blkPoy) << 3) + mv[1]; interpolator.getBlockChroma(ref.getPlaneData(comp), ref.getPlaneWidth(comp), ref.getPlaneHeight(comp), mbb[list].getPlaneData(comp), blkPoy * mb.getPlaneWidth(comp) + blkPox, mb.getPlaneWidth(comp), xx, yy, 2, 2); } } int blk4x4 = BLK8x8_BLOCKS[blk8x8][0]; mergePrediction(sh, vectors[0][blk4x4][2], vectors[1][blk4x4][2], predType[blk8x8], comp, mbb[0].getPlaneData(comp), mbb[1].getPlaneData(comp), BLK_8x8_MB_OFF_CHROMA[blk8x8], mb.getPlaneWidth(comp), 4, 4, mb.getPlaneData(comp), refs, poc); } } }