/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2012 Ausenco Engineering Canada Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.video.vp8;
import java.nio.ByteBuffer;
/**
* Basic VP8 decoder, holds all information needed to decode a stream of frames
*
*/
@SuppressWarnings("unused")
public class Decoder {
private final static int CURRENT_FRAME = 0;
private final static int LAST_FRAME = 1;
private final static int GOLDEN_FRAME = 2;
private final static int ALTREF_FRAME = 3;
// Header information
private boolean isKeyFrame;
private int bitstreamVersion;
private boolean isShown;
private BoolDecoder p0Dec;
private int frameWidth;
private int frameHeight;
private int widthScale;
private int heightScale;
private int mbRows;
private int mbCols;
//private boolean frameSizeUpdated;
// TODO add segment header info
private boolean simpleFilter;
private int filterLevel;
private int sharpness;
private boolean filterDeltaEnabled;
private final int refFilterDelta[] = new int[4];
private final int modeFilterDelta[] = new int[4];
private int numTokenPartitions;
private BoolDecoder tokenDecoders[];
// Dequant info
private int y1ACDeqIndex;
//private boolean hasDequantDeltas;
private int y1DCDelta;
private int y2ACDelta;
private int y2DCDelta;
private int uvACDelta;
private int uvDCDelta;
// Reference info
private boolean refreshLast;
private boolean refreshGolden;
private boolean refreshAltRef;
private int copyGolden;
private int copyAltRef;
private final boolean[] signBiases = new boolean[4];
private boolean refreshEntropy;
private TokenProbs tokenProbs;
private TokenProbs savedProbs;
private boolean saveProbsValid;
private boolean skipCoeffEnabled;
private int skipCoeffProb;
private final int yModeProbs[] = new int[4];
private final int uvModeProbs[] = new int[3];
private static class QuantFactor {
int y1AC;
int y1DC;
int y2AC;
int y2DC;
int uvAC;
int uvDC;
}
private QuantFactor qf;
private static class MBInfo {
int yMode;
int uvMode;
int segID;
int refFrame;
boolean skipCoeff;
int splitMVPart;
short mvX;
short mvY;
int eobMask; // TODO figure out what this does...
int[] subBlockModes = new int[16];
}
private MBInfo[] mbInfos;
private MBInfo dummyMBInfo;
private static class TokenEnt {
int[] v = new int[9];
TokenEnt() {
for (int i = 0; i < 9; ++i) { v[i] = 0; }
}
void clear(boolean clearY2) {
for (int i = 0; i < 8; ++i) { v[i] = 0; }
if (clearY2) { v[8] = 0; }
}
}
static class Tokens {
short[] v = new short[16];
}
static class MBTokens {
Tokens t[] = new Tokens[25];
boolean hasValues = false;
MBTokens() {
for (int i = 0; i < t.length; ++i) {
t[i] = new Tokens();
}
}
}
private MBTokens mbTokens[];
private final short[] temp = new short[16];
private static class DequantFactors {
int index = -1;
short factors[][] = new short[3][];
//init
{
for (int i = 0; i < 3; ++i) {
factors[i] = new short[2];
}
}
}
public YUVImage currentFrame;
public YUVImage lastFrame;
public YUVImage goldenFrame;
public YUVImage altRefFrame;
public YUVImage predRef;
public YUVImage finalRef;
public boolean testPred = false;
private final DequantFactors deqFactors[] = new DequantFactors[4];
//init
{
for (int i = 0; i < 4; ++i) {
deqFactors[i] = new DequantFactors();
}
dummyMBInfo = new MBInfo();
dummyMBInfo.yMode = Defs.B_PRED;
for (int i = 0; i < 16; ++i) {
dummyMBInfo.subBlockModes[i] = Defs.B_DC_PRED;
}
}
public Decoder() {
}
private int getBits(long data, int bitOffset, int bitLength) {
int mask = ~((-1)<<(bitLength));
return (int)((data >> bitOffset) & mask);
}
private void decodeFrameHeader(ByteBuffer frameData) throws VP8Exception {
if (frameData.limit() < 10) {
throw new VP8Exception(String.format("Frame too small: %d bytes", frameData.limit()));
}
long rawData = 0;
rawData += Util.getUByte(frameData);
rawData += (Util.getUByte(frameData) << 8);
rawData += (Util.getUByte(frameData) << 16);
isKeyFrame = (getBits(rawData, 0, 1) == 0);
bitstreamVersion = getBits(rawData, 1, 3);
if (bitstreamVersion > 3) {
throw new VP8Exception(String.format("Invalid bitstream version: %d", bitstreamVersion));
}
isShown = (getBits(rawData, 4, 1) == 1);
int part0Size = getBits(rawData, 5, 19);
if (isKeyFrame) {
keyFrameClearState();
if (Util.getUByte(frameData) != 0x9d || Util.getUByte(frameData) != 0x01 || Util.getUByte(frameData) != 0x2a) {
throw new VP8Exception("Magic code not found in keyframe");
}
rawData = 0;
rawData += Util.getUByte(frameData);
rawData += (Util.getUByte(frameData) << 8);
rawData += (Util.getUByte(frameData) << 16);
rawData += (Util.getUByte(frameData) << 24);
goldenFrame = null;
altRefFrame = null;
lastFrame = null;
// For now re-allocate the current buffer every frame (yeah, this is wasteful)
frameWidth = getBits(rawData, 0, 14);
widthScale = getBits(rawData, 14, 2);
frameHeight = getBits(rawData, 16, 14);
heightScale = getBits(rawData, 30, 2);
mbCols = (frameWidth + 15) >> 4;
mbRows = (frameHeight + 15) >> 4;
// Make the buffer fit whole macro blocks
currentFrame = new YUVImage(mbCols << 4, mbRows << 4);
mbInfos = new MBInfo[mbCols * mbRows];
mbTokens = new MBTokens[mbCols * mbRows];
for (int i = 0; i < mbRows * mbCols; ++i) {
mbInfos[i] = new MBInfo();
mbTokens[i] = new MBTokens();
}
}
if (part0Size > frameData.limit() - frameData.position()) {
throw new VP8Exception(String.format("Incomplete frame expected: %d more bytes", part0Size));
}
ByteBuffer part0Buffer = frameData.slice();
part0Buffer.limit(part0Size);
p0Dec = new BoolDecoder(part0Buffer);
frameData.position(frameData.position() + part0Size);
}
private void decodeLoopFilterHeader() {
simpleFilter = p0Dec.getFlag();
filterLevel = p0Dec.getLitUInt(6);
sharpness = p0Dec.getLitUInt(3);
filterDeltaEnabled = p0Dec.getFlag();
if (filterDeltaEnabled && p0Dec.getFlag()) {
for (int i = 0; i < 4; ++i) {
refFilterDelta[i] = p0Dec.getOptInt(6);
}
for (int i = 0; i < 4; ++i) {
modeFilterDelta[i] = p0Dec.getOptInt(6);
}
}
}
private void decodePartionInfo(ByteBuffer frameData) throws VP8Exception {
numTokenPartitions = p0Dec.getLitUInt(2);
if (numTokenPartitions != 0) throw new VP8Exception(String.format("Invalid number of partitions: %d", numTokenPartitions));
tokenDecoders = new BoolDecoder[1];
tokenDecoders[0] = new BoolDecoder(frameData);
}
private void decodeDequantHeader() {
y1ACDeqIndex = p0Dec.getLitUInt(7);
y1DCDelta = p0Dec.getOptInt(4);
y2DCDelta = p0Dec.getOptInt(4);
y2ACDelta = p0Dec.getOptInt(4);
uvDCDelta = p0Dec.getOptInt(4);
uvACDelta = p0Dec.getOptInt(4);
qf = new QuantFactor();
qf.y1DC = Defs.DC_Q_LOOKUP[y1ACDeqIndex + y1DCDelta];
qf.y1AC = Defs.AC_Q_LOOKUP[y1ACDeqIndex];
qf.y2DC = Defs.DC_Q_LOOKUP[y1ACDeqIndex + y2DCDelta] * 2;
qf.y2AC = Defs.AC_Q_LOOKUP[y1ACDeqIndex + y2ACDelta] * 155 / 100;
qf.uvDC = Defs.DC_Q_LOOKUP[y1ACDeqIndex + uvDCDelta];
qf.uvAC = Defs.AC_Q_LOOKUP[y1ACDeqIndex + uvACDelta];
if (qf.y2AC < 8) { qf.y2AC = 8; }
}
private void decodeReferenceHeader() {
if (isKeyFrame) {
refreshGolden = true;
refreshAltRef = true;
copyGolden = 0;
copyAltRef = 0;
signBiases[GOLDEN_FRAME] = false;
signBiases[ALTREF_FRAME] = false;
refreshEntropy = p0Dec.getFlag();
refreshLast = true;
} else {
refreshGolden = p0Dec.getFlag();
refreshAltRef = p0Dec.getFlag();
copyGolden = 0;
if (!refreshGolden)
copyGolden = p0Dec.getLitUInt(2);
copyAltRef = 0;
if (!refreshAltRef)
copyAltRef = p0Dec.getLitUInt(2);
signBiases[GOLDEN_FRAME] = p0Dec.getFlag();
signBiases[ALTREF_FRAME] = p0Dec.getFlag();
refreshEntropy = p0Dec.getFlag();
refreshLast = p0Dec.getFlag();
}
}
private void decodeEntropyHeader() {
tokenProbs.updateProbs(p0Dec);
skipCoeffEnabled = p0Dec.getFlag();
if (skipCoeffEnabled)
skipCoeffProb = p0Dec.getLitUInt(8);
// TODO: parse interframe probability updates here
assert(isKeyFrame);
}
private int getAboveBMode(MBInfo curr, MBInfo above, int i) {
if (i < 4) {
if (above.yMode == Defs.DC_PRED) return Defs.B_DC_PRED;
if (above.yMode == Defs.H_PRED) return Defs.B_H_PRED;
if (above.yMode == Defs.V_PRED) return Defs.B_V_PRED;
if (above.yMode == Defs.TM_PRED) return Defs.B_TM_PRED;
if (above.yMode == Defs.B_PRED) return above.subBlockModes[i+12];
assert(false);
}
return curr.subBlockModes[i-4];
}
private int getLeftBMode(MBInfo curr, MBInfo left, int i) {
if ((i & 3) == 0) {
if (left.yMode == Defs.DC_PRED) return Defs.B_DC_PRED;
if (left.yMode == Defs.H_PRED) return Defs.B_H_PRED;
if (left.yMode == Defs.V_PRED) return Defs.B_V_PRED;
if (left.yMode == Defs.TM_PRED) return Defs.B_TM_PRED;
if (left.yMode == Defs.B_PRED) return left.subBlockModes[i+3];
assert(false);
}
return curr.subBlockModes[i-1];
}
private void decodeMBPred(MBInfo currMB, MBInfo above, MBInfo left) {
// TODO: read segment here when applicable
if (skipCoeffEnabled) {
currMB.skipCoeff = p0Dec.decodeBit(skipCoeffProb) == 1;
}
// TODO handle inter prediction here
currMB.yMode = p0Dec.getTreeVal(Defs.KF_Y_MODE_TREE, Defs.KF_Y_MODE_PROBS);
//expectIntraMode(currMB.yMode);
if (currMB.yMode == Defs.B_PRED) {
for (int i = 0; i < 16; ++i) {
int aMode = getAboveBMode(currMB, above, i);
int lMode = getLeftBMode(currMB, left, i);
//Verifier.demand(String.format("BMODE %d %d", aMode, lMode));
currMB.subBlockModes[i] = p0Dec.getTreeVal(Defs.B_MODE_TREE, Defs.B_MODE_PROBS[aMode][lMode]);
//expectSubblockMode(currMB.subBlockModes[i]);
}
}
currMB.uvMode = p0Dec.getTreeVal(Defs.UV_MODE_TREE, Defs.KF_UV_MODE_PROBS);
//expectIntraMode(currMB.uvMode);
}
public void decodeFrame(ByteBuffer frameData) throws VP8Exception {
decodeFrameHeader(frameData);
if (isKeyFrame) {
// read colour and clamping info
boolean color = p0Dec.getFlag();
if (color) throw new VP8Exception("Unsupported colour space");
p0Dec.getFlag(); // ignore clamp
}
if (p0Dec.getFlag()) {
throw new VP8Exception("Segmentation not supported");
}
decodeLoopFilterHeader();
decodePartionInfo(frameData);
decodeDequantHeader();
decodeReferenceHeader();
if (isKeyFrame) {
tokenProbs = new TokenProbs();
}
if (!refreshEntropy) {
// Save the current entropy values
savedProbs = new TokenProbs(tokenProbs);
} else {
savedProbs = null;
}
if (isKeyFrame) {
yModeProbs[0] = 112;
yModeProbs[1] = 86;
yModeProbs[2] = 140;
yModeProbs[3] = 37;
uvModeProbs[0] = 162;
uvModeProbs[1] = 101;
uvModeProbs[2] = 204;
}
decodeEntropyHeader();
for (int y = 0; y < mbRows; ++y) {
for (int x = 0; x < mbCols; ++x) {
MBInfo aboveMB = (y == 0) ? dummyMBInfo : mbInfos[(y-1)*mbCols + x];
MBInfo leftMB = (x == 0) ? dummyMBInfo : mbInfos[y*mbCols + x - 1];
MBInfo currMB = mbInfos[y*mbCols + x];
decodeMBPred(currMB, aboveMB, leftMB);
}
}
TokenEnt[] aboveEnts = new TokenEnt[mbCols];
for (int i = 0; i < mbCols; ++i) {
aboveEnts[i] = new TokenEnt();
}
//Verifier.demand(String.format("Y1DC: %d", qf.y1DC));
//Verifier.demand(String.format("Y1AC: %d", qf.y1AC));
//Verifier.demand(String.format("UVDC: %d", qf.uvDC));
//Verifier.demand(String.format("UVAC: %d", qf.uvAC));
//Verifier.demand(String.format("Y2DC: %d", qf.y2DC));
//Verifier.demand(String.format("Y2AC: %d", qf.y2AC));
// Now decode DCT tokens
for (int y = 0; y < mbRows; ++y) {
TokenEnt leftEnt = new TokenEnt();
for (int x = 0; x < mbCols; ++x) {
MBInfo mbi = mbInfos[y*mbCols + x];
MBTokens mbt = mbTokens[y*mbCols + x];
decodeMBTokens(tokenDecoders[0], mbi, mbt, aboveEnts[x], leftEnt);
// Verifier.demand("COEFFS:");
// for (int i = 0; i < 25; ++i) {
// for (int j = 0; j < 16; ++j) {
// Verifier.demand(String.format("%d", mbt.t[i].v[j]));
// }
// }
}
}
// Everything has been read, start reconstructing it
for (int yMB = 0; yMB < mbRows; ++yMB) {
for (int xMB = 0; xMB < mbCols; ++xMB) {
predictIntra(xMB, yMB);
addResidue(xMB, yMB);
}
}
}
private void addResidue(int xMB, int yMB) {
MBInfo mbi = mbInfos[yMB*mbCols + xMB];
MBTokens tokens = mbTokens[yMB*mbCols + xMB];
short[] out = new short[16];
if (mbi.yMode != Defs.B_PRED) {
// Fixup DC
fixupDC(tokens);
// Add luma
// Node, B_PRED adds luma residue as it predicts
for (int i = 0; i < 16; ++i) {
int x = xMB*16 + (i&3)*4;
int y = yMB*16 + (i>>2)*4;
Transform.deDCT(tokens.t[i].v, out, temp);
Util.addResidueToPlane(x, y, out, mbCols*16, currentFrame.yPlane);
testPred(x, y, 0, true);
}
}
// Add chroma
for (int i = 0; i < 4; ++i) {
int x = xMB*8 + (i&1)*4;
int y = yMB*8 + (i>>1)*4;
Transform.deDCT(tokens.t[i+16].v, out, temp);
Util.addResidueToPlane(x, y, out, mbCols*8, currentFrame.uPlane);
Transform.deDCT(tokens.t[i+20].v, out, temp);
Util.addResidueToPlane(x, y, out, mbCols*8, currentFrame.vPlane);
}
testPred(xMB*8, yMB*8, 1, true);
testPred(xMB*8, yMB*8, 2, true);
}
private void fixupDC(MBTokens tokens) {
short[] out = new short[16];
Transform.deWHT(tokens.t[24].v, out, temp);
for (int i = 0; i < 16; ++i) {
tokens.t[i].v[0] = out[i];
}
}
private void predictIntra(int xMB, int yMB) {
MBInfo mbi = mbInfos[yMB*mbCols + xMB];
short[] residue = new short[16];
// Start with Y
switch (mbi.yMode) {
case Defs.DC_PRED:
Pred.predictDC(xMB*16, yMB*16, true, currentFrame.width, currentFrame.yPlane);
break;
case Defs.V_PRED:
Pred.predictV(xMB*16, yMB*16, true, currentFrame.width, currentFrame.yPlane);
break;
case Defs.H_PRED:
Pred.predictH(xMB*16, yMB*16, true, currentFrame.width, currentFrame.yPlane);
break;
case Defs.TM_PRED:
Pred.predictTM(xMB*16, yMB*16, true, currentFrame.width, currentFrame.yPlane);
break;
case Defs.B_PRED:
// Need to predict then add residue to the frame one sub block at a time
MBTokens mbt = mbTokens[yMB*mbCols + xMB];
for (int i = 0; i < 16; ++i) {
int x = xMB*16 + (i&3)*4;
int y = yMB*16 + (i>>2)*4;
Pred.predictBSubBlock(xMB*16, yMB*16, x, y, mbi.subBlockModes[i], mbCols*16, currentFrame.yPlane);
Transform.deDCT(mbt.t[i].v, residue, temp);
Util.addResidueToPlane(x, y, residue, mbCols*16, currentFrame.yPlane);
testPred(x, y, 0, true);
}
break;
default:
assert(false);
}
// Check that this block is what we expected
if (mbi.yMode != Defs.B_PRED) {
testPred(xMB*16, yMB*16, 0, false);
}
// chroma
switch (mbi.uvMode) {
case Defs.DC_PRED:
Pred.predictDC(xMB*8, yMB*8, false, currentFrame.width >> 1, currentFrame.uPlane);
Pred.predictDC(xMB*8, yMB*8, false, currentFrame.width >> 1, currentFrame.vPlane);
break;
case Defs.V_PRED:
Pred.predictV(xMB*8, yMB*8, false, currentFrame.width >> 1, currentFrame.uPlane);
Pred.predictV(xMB*8, yMB*8, false, currentFrame.width >> 1, currentFrame.vPlane);
break;
case Defs.H_PRED:
Pred.predictH(xMB*8, yMB*8, false, currentFrame.width >> 1, currentFrame.uPlane);
Pred.predictH(xMB*8, yMB*8, false, currentFrame.width >> 1, currentFrame.vPlane);
break;
case Defs.TM_PRED:
Pred.predictTM(xMB*8, yMB*8, false, currentFrame.width >> 1, currentFrame.uPlane);
Pred.predictTM(xMB*8, yMB*8, false, currentFrame.width >> 1, currentFrame.vPlane);
break;
default:
assert(false);
}
testPred(xMB*8, yMB*8, 1, false);
testPred(xMB*8, yMB*8, 2, false);
}
public void testPred(int x, int y, int planeInd, boolean withResidue) {
if (!testPred) {
return;
}
int blockWidth = 0;
int stride = 0;
YUVImage refImage = withResidue ? finalRef : predRef;
byte[] refPlane = null;
byte[] plane = null;
switch (planeInd) {
case 0:
refPlane = refImage.yPlane;
plane = currentFrame.yPlane;
blockWidth = 16;
stride = predRef.width;
break;
case 1:
refPlane = refImage.uPlane;
plane = currentFrame.uPlane;
blockWidth = 8;
stride = predRef.width >> 1;
break;
case 2:
refPlane = refImage.vPlane;
plane = currentFrame.vPlane;
blockWidth = 8;
stride = predRef.width >> 1;
break;
case 3:
refPlane = refImage.yPlane;
plane = currentFrame.yPlane;
blockWidth = 4;
stride = predRef.width;
break;
default: assert(false);
}
StringBuilder refBytes = new StringBuilder();
StringBuilder ourBytes = new StringBuilder();
boolean failure = false;
int firstX = 0;
int firstY = 0;
for (int j = 0; j < blockWidth; ++j) {
for (int i = 0; i < blockWidth; ++i) {
int rx = x + i;
int ry = y + j;
int refVal = Util.getUByte(refPlane, ry*stride+rx);
int val = Util.getUByte(plane, ry*stride+rx);
refBytes.append(String.format(" %3d,", refVal));
ourBytes.append(String.format(" %3d,", val));
if (val != refVal) {
if (!failure) {
firstX = i;
firstY = j;
}
failure = true;
}
}
refBytes.append("\n");
ourBytes.append("\n");
}
if (failure) {
System.out.printf("Failure at Block (%d, %d) first (%d, %d) plane: %d\n", x, y, firstX, firstY, planeInd);
System.out.printf("Expected:\n%s", refBytes.toString());
System.out.printf("Got:\n%s", ourBytes.toString());
assert(false);
}
}
private void decodeMBTokens(BoolDecoder dec, MBInfo mbi, MBTokens mbt, TokenEnt above, TokenEnt left) {
boolean hasY2 = mbi.yMode != Defs.B_PRED; // TODO SPLITMV
if (mbi.skipCoeff) {
left.clear(hasY2);
above.clear(hasY2);
return;
}
if (mbi.yMode != Defs.B_PRED) {
// Decode Y2
decodeTokens(dec, 1, 24, mbt, above, left, 0, qf.y2DC, qf.y2AC);
}
// Decode the Y blocks
for (int i = 0; i < 16; ++i) {
decodeTokens(dec, (hasY2?0:3), i, mbt, above, left, (hasY2?1:0), qf.y1DC, qf.y1AC);
}
for (int i = 0; i < 8; ++i) {
decodeTokens(dec, 2, i+16, mbt, above, left, 0, qf.uvDC, qf.uvAC);
}
}
private void decodeTokens(BoolDecoder dec, int type, int blockInd, MBTokens mbt, TokenEnt above, TokenEnt left, int startCoeff,
int dcDQ, int acDQ) {
int c = above.v[Defs.BLOCK_TO_ABOVE_ENT[blockInd]] + left.v[Defs.BLOCK_TO_LEFT_ENT[blockInd]];
boolean lastTokZero = false;
boolean hasVal = false;
for (int i = startCoeff; i < 16; ++i) {
int b = Defs.BANDS[i];
int[] probs = tokenProbs.getProbs(type, b, c);
int dct = dec.getTreeVal(Defs.TOKEN_TREE, probs, lastTokZero ? 2 : 0);
if (dct == Defs.DCT_EOB) {
break;
}
int val = 0;
hasVal = hasVal || dct > Defs.DCT_0;
lastTokZero = (dct == Defs.DCT_0);
if (dct == Defs.DCT_0) { c = 0; }
else if (dct == Defs.DCT_1) { c = 1; }
else { c = 2; }
switch (dct) {
case Defs.DCT_0:
val = 0;
break;
case Defs.DCT_1:
//Verifier.demand("DCT_1");
val = 1;
break;
case Defs.DCT_2:
//Verifier.demand("DCT_2");
val = 2;
break;
case Defs.DCT_3:
//Verifier.demand("DCT_3");
val = 3;
break;
case Defs.DCT_4:
//Verifier.demand("DCT_4");
val = 4;
break;
case Defs.DCT_CAT1:
//Verifier.demand("DCT_CAT1");
val = 5 + dec.getIntWithProbs(Defs.CAT1_PROBS, 1);
break;
case Defs.DCT_CAT2:
//Verifier.demand("DCT_CAT2");
val = 7 + dec.getIntWithProbs(Defs.CAT2_PROBS, 2);
break;
case Defs.DCT_CAT3:
//Verifier.demand("DCT_CAT3");
val = 11 + dec.getIntWithProbs(Defs.CAT3_PROBS, 3);
break;
case Defs.DCT_CAT4:
//Verifier.demand("DCT_CAT4");
val = 19 + dec.getIntWithProbs(Defs.CAT4_PROBS, 4);
break;
case Defs.DCT_CAT5:
//Verifier.demand("DCT_CAT5");
val = 35 + dec.getIntWithProbs(Defs.CAT5_PROBS, 5);
break;
case Defs.DCT_CAT6:
//Verifier.demand("DCT_CAT6");
val = 67 + dec.getIntWithProbs(Defs.CAT6_PROBS, 11);
break;
}
if (val != 0) {
//Verifier.demand(String.format("VAL: %d", val));
if (dec.getFlag()) { val = -val; }
}
int dqf = (i == 0) ? dcDQ : acDQ;
int coeffInd = Defs.ZIGZAG[i];
mbt.t[blockInd].v[coeffInd] = (short)(val * dqf);
}
int entVal = hasVal ? 1 : 0;
above.v[Defs.BLOCK_TO_ABOVE_ENT[blockInd]] = entVal;
left.v[Defs.BLOCK_TO_LEFT_ENT[blockInd]] = entVal;
mbt.hasValues = hasVal;
}
private void keyFrameClearState() {
for (int i = 0; i < 4; ++i) {
refFilterDelta[i] = 0;
modeFilterDelta[i] = 0;
}
}
/*
private void expectIntraMode(int mode) {
switch (mode) {
case DC_PRED:
Verifier.demand("DC_PRED"); return;
case H_PRED:
Verifier.demand("H_PRED"); return;
case V_PRED:
Verifier.demand("V_PRED"); return;
case TM_PRED:
Verifier.demand("TM_PRED"); return;
case B_PRED:
Verifier.demand("B_PRED"); return;
}
assert(false);
} */
/*
private void expectSubblockMode(int mode) {
switch (mode) {
case B_DC_PRED:
Verifier.demand("B_DC_PRED"); return;
case B_V_PRED:
Verifier.demand("B_V_PRED"); return;
case B_H_PRED:
Verifier.demand("B_H_PRED"); return;
case B_TM_PRED:
Verifier.demand("B_TM_PRED"); return;
case B_LD_PRED:
Verifier.demand("B_LD_PRED"); return;
case B_RD_PRED:
Verifier.demand("B_RD_PRED"); return;
case B_VR_PRED:
Verifier.demand("B_VR_PRED"); return;
case B_VL_PRED:
Verifier.demand("B_VL_PRED"); return;
case B_HD_PRED:
Verifier.demand("B_HD_PRED"); return;
case B_HU_PRED:
Verifier.demand("B_HU_PRED"); return;
}
assert(false);
} */
}