package org.jcodec.codecs.common.biari; import java.io.IOException; import java.nio.ByteBuffer; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * H264 CABAC M-encoder * * @author The JCodec project * */ public class MEncoder { private ByteBuffer out; // Encoder state private int range; private int offset; // Carry propagation related private int onesOutstanding; private boolean zeroBorrowed; // Output related private int outReg; private int bitsInOutReg; private int[][] models; public MEncoder(ByteBuffer out, int[][] models) { range = 510; this.models = models; this.out = out; } /** * Encodes one bin in normal mode using supplied context model * * @param bin * @param cm * @throws IOException */ public void encodeBin(int model, int bin) { int qs = (range >> 6) & 0x3; int rangeLPS = MConst.rangeLPS[qs][models[0][model]]; range -= rangeLPS; if (bin != models[1][model]) { offset += range; range = rangeLPS; if (models[0][model] == 0) models[1][model] = 1 - models[1][model]; models[0][model] = MConst.transitLPS[models[0][model]]; } else { if (models[0][model] < 62) models[0][model] ++; } renormalize(); } /** * Codes one bin in bypass mode for symbols with uniform probability * distribution * * @param bin * @throws IOException */ public void encodeBinBypass(int bin) { offset <<= 1; if (bin == 1) { offset += range; } if ((offset & 0x400) != 0) { flushOutstanding(1); offset &= 0x3ff; } else if ((offset & 0x200) != 0) { offset &= 0x1ff; ++onesOutstanding; } else { flushOutstanding(0); } } /** * Codes termination flag. Range for LPS is preset to be 2 * * @param bin * @throws IOException */ public void encodeBinFinal(int bin) { range -= 2; if (bin == 0) { renormalize(); } else { offset += range; range = 2; renormalize(); } } public void finishEncoding() { flushOutstanding((offset >> 9) & 0x1); putBit((offset >> 8) & 0x1); stuffBits(); } private void renormalize() { while (range < 256) { if (offset < 256) { flushOutstanding(0); } else if (offset < 512) { offset &= 0xff; ++onesOutstanding; } else { offset &= 0x1ff; flushOutstanding(1); } range <<= 1; offset <<= 1; } } private void flushOutstanding(int hasCarry) { if (zeroBorrowed) putBit(hasCarry); int trailingBit = 1 - hasCarry; for (; onesOutstanding > 0; onesOutstanding--) putBit(trailingBit); zeroBorrowed = true; } private void putBit(int bit) { outReg = (outReg << 1) | bit; ++bitsInOutReg; if (bitsInOutReg == 8) { out.put((byte) outReg); outReg = 0; bitsInOutReg = 0; } } private void stuffBits() { if (bitsInOutReg == 0) { out.put((byte) 128); } else { outReg = (outReg << 1) | 1; outReg <<= (8 - (bitsInOutReg + 1)); out.put((byte) outReg); outReg = 0; bitsInOutReg = 0; } } }