package org.jcodec.codecs.h264.decode; import static java.lang.System.arraycopy; import static org.jcodec.codecs.h264.H264Const.PartPred.L0; import static org.jcodec.codecs.h264.decode.MBlockDecoderUtils.debugPrint; import static org.jcodec.codecs.h264.io.model.MBType.B_Direct_16x16; import static org.jcodec.codecs.h264.io.model.MBType.P_16x16; import static org.jcodec.codecs.h264.io.model.MBType.P_16x8; import static org.jcodec.codecs.h264.io.model.MBType.P_8x16; import static org.jcodec.codecs.h264.io.model.MBType.P_8x8; import static org.jcodec.codecs.h264.io.model.MBType.P_8x8ref0; import static org.jcodec.codecs.h264.io.model.SliceType.P; import org.jcodec.codecs.h264.H264Const; import org.jcodec.codecs.h264.decode.aso.MapManager; import org.jcodec.codecs.h264.decode.aso.Mapper; import org.jcodec.codecs.h264.io.model.Frame; import org.jcodec.codecs.h264.io.model.MBType; import org.jcodec.codecs.h264.io.model.SeqParameterSet; import org.jcodec.codecs.h264.io.model.SliceHeader; import org.jcodec.codecs.h264.io.model.SliceType; import org.jcodec.common.IntObjectMap; import org.jcodec.common.logging.Logger; import org.jcodec.common.model.Picture8Bit; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * A decoder for an individual slice * * @author The JCodec project * */ public class SliceDecoder { private Mapper mapper; private MBlockDecoderIntra16x16 decoderIntra16x16; private MBlockDecoderIntraNxN decoderIntraNxN; private MBlockDecoderInter decoderInter; private MBlockDecoderInter8x8 decoderInter8x8; private MBlockSkipDecoder skipDecoder; private MBlockDecoderBDirect decoderBDirect; private RefListManager refListManager; private MBlockDecoderIPCM decoderIPCM; private SliceReader parser; private SeqParameterSet activeSps; private Frame frameOut; private DecoderState decoderState; private DeblockerInput di; private IntObjectMap<Frame> lRefs; private Frame[] sRefs; public SliceDecoder(SeqParameterSet activeSps, Frame[] sRefs, IntObjectMap<Frame> lRefs, DeblockerInput di, Frame result) { this.di = di; this.activeSps = activeSps; this.frameOut = result; this.sRefs = sRefs; this.lRefs = lRefs; } public void decodeFromReader(SliceReader sliceReader) { parser = sliceReader; initContext(); debugPrint("============%d============= ", frameOut.getPOC()); Frame[][] refList = refListManager.getRefList(); decodeMacroblocks(refList); } private void initContext() { SliceHeader sh = parser.getSliceHeader(); decoderState = new DecoderState(sh); mapper = new MapManager(sh.sps, sh.pps).getMapper(sh); decoderIntra16x16 = new MBlockDecoderIntra16x16(mapper, sh, di, frameOut.getPOC(), decoderState); decoderIntraNxN = new MBlockDecoderIntraNxN(mapper, sh, di, frameOut.getPOC(), decoderState); decoderInter = new MBlockDecoderInter(mapper, sh, di, frameOut.getPOC(), decoderState); decoderBDirect = new MBlockDecoderBDirect(mapper, sh, di, frameOut.getPOC(), decoderState); decoderInter8x8 = new MBlockDecoderInter8x8(mapper, decoderBDirect, sh, di, frameOut.getPOC(), decoderState); skipDecoder = new MBlockSkipDecoder(mapper, decoderBDirect, sh, di, frameOut.getPOC(), decoderState); decoderIPCM = new MBlockDecoderIPCM(mapper, decoderState); refListManager = new RefListManager(sh, sRefs, lRefs, frameOut); } private void decodeMacroblocks(Frame[][] refList) { Picture8Bit mb = Picture8Bit.create(16, 16, activeSps.chroma_format_idc); int mbWidth = activeSps.pic_width_in_mbs_minus1 + 1; MBlock mBlock = new MBlock(activeSps.chroma_format_idc); while (parser.readMacroblock(mBlock)) { decode(mBlock, parser.getSliceHeader().slice_type, mb, refList); int mbAddr = mapper.getAddress(mBlock.mbIdx); int mbX = mbAddr % mbWidth; int mbY = mbAddr / mbWidth; putMacroblock(frameOut, mb, mbX, mbY); di.shs[mbAddr] = parser.getSliceHeader(); di.refsUsed[mbAddr] = refList; fillCoeff(mBlock, mbX, mbY); mb.fill(0); mBlock.clear(); } } private void fillCoeff(MBlock mBlock, int mbX, int mbY) { for (int i = 0; i < 16; i++) { int blkOffLeft = H264Const.MB_BLK_OFF_LEFT[i]; int blkOffTop = H264Const.MB_BLK_OFF_TOP[i]; int blkX = (mbX << 2) + blkOffLeft; int blkY = (mbY << 2) + blkOffTop; di.nCoeff[blkY][blkX] = mBlock.nCoeff[i]; } } public void decode(MBlock mBlock, SliceType sliceType, Picture8Bit mb, Frame[][] references) { if (mBlock.skipped) { skipDecoder.decodeSkip(mBlock, references, mb, sliceType); } else if (sliceType == SliceType.I) { decodeMBlockI(mBlock, mb); } else if (sliceType == SliceType.P) { decodeMBlockP(mBlock, mb, references); } else { decodeMBlockB(mBlock, mb, references); } } private void decodeMBlockI(MBlock mBlock, Picture8Bit mb) { decodeMBlockIInt(mBlock, mb); } private void decodeMBlockIInt(MBlock mBlock, Picture8Bit mb) { if (mBlock.curMbType == MBType.I_NxN) { decoderIntraNxN.decode(mBlock, mb); } else if (mBlock.curMbType == MBType.I_16x16) { decoderIntra16x16.decode(mBlock, mb); } else { Logger.warn("IPCM macroblock found. Not tested, may cause unpredictable behavior."); decoderIPCM.decode(mBlock, mb); } } private void decodeMBlockP(MBlock mBlock, Picture8Bit mb, Frame[][] references) { if (P_16x16 == mBlock.curMbType) { decoderInter.decode16x16(mBlock, mb, references, L0); } else if (P_16x8 == mBlock.curMbType) { decoderInter.decode16x8(mBlock, mb, references, L0, L0); } else if (P_8x16 == mBlock.curMbType) { decoderInter.decode8x16(mBlock, mb, references, L0, L0); } else if (P_8x8 == mBlock.curMbType) { decoderInter8x8.decode(mBlock, references, mb, P, false); } else if (P_8x8ref0 == mBlock.curMbType) { decoderInter8x8.decode(mBlock, references, mb, P, true); } else { decodeMBlockIInt(mBlock, mb); } } private void decodeMBlockB(MBlock mBlock, Picture8Bit mb, Frame[][] references) { if (mBlock.curMbType.isIntra()) { decodeMBlockIInt(mBlock, mb); } else { if (mBlock.curMbType == B_Direct_16x16) { decoderBDirect.decode(mBlock, mb, references); } else if (mBlock.mbType <= 3) { decoderInter.decode16x16(mBlock, mb, references, H264Const.bPredModes[mBlock.mbType][0]); } else if (mBlock.mbType == 22) { decoderInter8x8.decode(mBlock, references, mb, SliceType.B, false); } else if ((mBlock.mbType & 1) == 0) { decoderInter.decode16x8(mBlock, mb, references, H264Const.bPredModes[mBlock.mbType][0], H264Const.bPredModes[mBlock.mbType][1]); } else { decoderInter.decode8x16(mBlock, mb, references, H264Const.bPredModes[mBlock.mbType][0], H264Const.bPredModes[mBlock.mbType][1]); } } } public void putMacroblock(Picture8Bit tgt, Picture8Bit decoded, int mbX, int mbY) { byte[] luma = tgt.getPlaneData(0); int stride = tgt.getPlaneWidth(0); byte[] cb = tgt.getPlaneData(1); byte[] cr = tgt.getPlaneData(2); int strideChroma = tgt.getPlaneWidth(1); int dOff = 0; for (int i = 0; i < 16; i++) { arraycopy(decoded.getPlaneData(0), dOff, luma, (mbY * 16 + i) * stride + mbX * 16, 16); dOff += 16; } for (int i = 0; i < 8; i++) { arraycopy(decoded.getPlaneData(1), i * 8, cb, (mbY * 8 + i) * strideChroma + mbX * 8, 8); } for (int i = 0; i < 8; i++) { arraycopy(decoded.getPlaneData(2), i * 8, cr, (mbY * 8 + i) * strideChroma + mbX * 8, 8); } } }