package org.jcodec.codecs.h264.decode; import static org.jcodec.codecs.h264.H264Const.PartPred.Bi; import static org.jcodec.codecs.h264.H264Const.PartPred.L0; import static org.jcodec.codecs.h264.H264Const.PartPred.L1; import static org.jcodec.common.tools.MathUtil.clip; import org.jcodec.codecs.h264.H264Const.PartPred; import org.jcodec.codecs.h264.io.model.Frame; import org.jcodec.codecs.h264.io.model.PictureParameterSet; import org.jcodec.codecs.h264.io.model.PredictionWeightTable; import org.jcodec.codecs.h264.io.model.SliceHeader; import org.jcodec.codecs.h264.io.model.SliceType; import org.jcodec.common.tools.MathUtil; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Prediction merge and weight routines * * @author The JCodec project * */ public class Prediction { private SliceHeader sh; public Prediction(SliceHeader sh) { this.sh = sh; } public void mergePrediction(int refIdxL0, int refIdxL1, PartPred predType, int comp, int[] pred0, int[] pred1, int off, int stride, int blkW, int blkH, int[] out, Frame[][] refs, Frame thisFrame) { PictureParameterSet pps = sh.pps; if (sh.slice_type == SliceType.P) { if (pps.weighted_pred_flag && sh.pred_weight_table != null) { PredictionWeightTable w = sh.pred_weight_table; weight(pred0, stride, off, blkW, blkH, comp == 0 ? w.luma_log2_weight_denom : w.chroma_log2_weight_denom, comp == 0 ? w.luma_weight[0][refIdxL0] : w.chroma_weight[0][comp - 1][refIdxL0], comp == 0 ? w.luma_offset[0][refIdxL0] : w.chroma_offset[0][comp - 1][refIdxL0], out); } else { copyPrediction(pred0, stride, off, blkW, blkH, out); } } else { if (!pps.weighted_pred_flag || sh.pps.weighted_bipred_idc == 0 || (sh.pps.weighted_bipred_idc == 2 && predType != Bi)) { mergeAvg(pred0, pred1, stride, predType, off, blkW, blkH, out); } else if (sh.pps.weighted_bipred_idc == 1) { PredictionWeightTable w = sh.pred_weight_table; int w0 = refIdxL0 == -1 ? 0 : (comp == 0 ? w.luma_weight[0][refIdxL0] : w.chroma_weight[0][comp - 1][refIdxL0]); int w1 = refIdxL1 == -1 ? 0 : (comp == 0 ? w.luma_weight[1][refIdxL1] : w.chroma_weight[1][comp - 1][refIdxL1]); int o0 = refIdxL0 == -1 ? 0 : (comp == 0 ? w.luma_offset[0][refIdxL0] : w.chroma_offset[0][comp - 1][refIdxL0]); int o1 = refIdxL1 == -1 ? 0 : (comp == 0 ? w.luma_offset[1][refIdxL1] : w.chroma_offset[1][comp - 1][refIdxL1]); mergeWeight(pred0, pred1, stride, predType, off, blkW, blkH, comp == 0 ? w.luma_log2_weight_denom : w.chroma_log2_weight_denom, w0, w1, o0, o1, out); } else { int tb = MathUtil.clip(thisFrame.getPOC() - refs[0][refIdxL0].getPOC(), -128, 127); int td = MathUtil.clip(refs[1][refIdxL1].getPOC() - refs[0][refIdxL0].getPOC(), -128, 127); int w0 = 32, w1 = 32; if (td != 0 && refs[0][refIdxL0].isShortTerm() && refs[1][refIdxL1].isShortTerm()) { int tx = (16384 + Math.abs(td / 2)) / td; int dsf = clip((tb * tx + 32) >> 6, -1024, 1023) >> 2; if (dsf >= -64 && dsf <= 128) { w1 = dsf; w0 = 64 - dsf; } } mergeWeight(pred0, pred1, stride, predType, off, blkW, blkH, 5, w0, w1, 0, 0, out); } } } private void mergeAvg(int[] blk0, int[] blk1, int stride, PartPred p0, int off, int blkW, int blkH, int[] o) { if (p0 == Bi) mergePrediction(blk0, blk1, stride, p0, off, blkW, blkH, o); else if (p0 == L0) copyPrediction(blk0, stride, off, blkW, blkH, o); else if (p0 == L1) copyPrediction(blk1, stride, off, blkW, blkH, o); } private void mergeWeight(int[] blk0, int[] blk1, int stride, PartPred partPred, int off, int blkW, int blkH, int logWD, int w0, int w1, int o0, int o1, int[] out) { if (partPred == L0) { weight(blk0, stride, off, blkW, blkH, logWD, w0, o0, out); } else if (partPred == L1) { weight(blk1, stride, off, blkW, blkH, logWD, w1, o1, out); } else if (partPred == Bi) { weightPrediction(blk0, blk1, stride, off, blkW, blkH, logWD, w0, w1, o0, o1, out); } } private void copyPrediction(int[] in, int stride, int off, int blkW, int blkH, int[] o) { for (int i = 0; i < blkH; i++, off += stride - blkW) for (int j = 0; j < blkW; j++, off++) o[off] = in[off]; } private void mergePrediction(int[] blk0, int[] blk1, int stride, PartPred p0, int off, int blkW, int blkH, int[] o) { for (int i = 0; i < blkH; i++, off += stride - blkW) for (int j = 0; j < blkW; j++, off++) o[off] = (blk0[off] + blk1[off] + 1) >> 1; } private void weightPrediction(int[] blk0, int[] blk1, int stride, int off, int blkW, int blkH, int logWD, int w0, int w1, int o0, int o1, int[] out) { int dvadva = 1 << logWD; int sum = (o0 + o1 + 1) >> 1; int logWDCP1 = logWD + 1; for (int i = 0; i < blkH; i++, off += stride - blkW) for (int j = 0; j < blkW; j++, off++) { out[off] = MathUtil.clip(((blk0[off] * w0 + blk1[off] * w1 + dvadva) >> logWDCP1) + sum, 0, 255); } } private void weight(int[] blk0, int stride, int off, int blkW, int blkH, int logWD, int w, int o, int[] out) { int dva = 1 << (logWD - 1); if (logWD >= 1) { for (int i = 0; i < blkH; i++, off += stride - blkW) for (int j = 0; j < blkW; j++, off++) out[off] = MathUtil.clip(((blk0[off] * w + dva) >> logWD) + o, 0, 255); } else { for (int i = 0; i < blkH; i++, off += stride - blkW) for (int j = 0; j < blkW; j++, off++) out[off] = MathUtil.clip(blk0[off] * w + o, 0, 255); } } }