www in docker support

This commit is contained in:
MaddoScientisto 2026-04-22 18:41:37 +02:00
commit c227fce036
2145 changed files with 399596 additions and 58 deletions

View file

@ -0,0 +1,234 @@
package org.jcodec.codecs.h264;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.codecs.h264.decode.SliceHeaderReader;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.Demuxer;
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.IntObjectMap;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.model.Packet;
public class BufferH264ES implements DemuxerTrack, Demuxer {
private ByteBuffer bb;
private IntObjectMap<PictureParameterSet> pps;
private IntObjectMap<SeqParameterSet> sps;
private int prevFrameNumOffset;
private int prevFrameNum;
private int prevPicOrderCntMsb;
private int prevPicOrderCntLsb;
private int frameNo;
public BufferH264ES(ByteBuffer bb) {
this.pps = new IntObjectMap<>();
this.sps = new IntObjectMap<>();
this.bb = bb;
this.frameNo = 0;
}
public Packet nextFrame() {
ByteBuffer result = this.bb.duplicate();
NALUnit prevNu = null;
SliceHeader prevSh = null;
while (true) {
this.bb.mark();
ByteBuffer buf = H264Utils.nextNALUnit(this.bb);
if (buf == null)
break;
NALUnit nu = NALUnit.read(buf);
if (nu.type == NALUnitType.IDR_SLICE || nu.type == NALUnitType.NON_IDR_SLICE) {
SliceHeader sh = readSliceHeader(buf, nu);
if (prevNu != null && prevSh != null && !sameFrame(prevNu, nu, prevSh, sh)) {
this.bb.reset();
break;
}
prevSh = sh;
prevNu = nu;
continue;
}
if (nu.type == NALUnitType.PPS) {
PictureParameterSet read = PictureParameterSet.read(buf);
this.pps.put(read.picParameterSetId, read);
continue;
}
if (nu.type == NALUnitType.SPS) {
SeqParameterSet read = SeqParameterSet.read(buf);
this.sps.put(read.seqParameterSetId, read);
}
}
result.limit(this.bb.position());
return (prevSh == null) ? null : detectPoc(result, prevNu, prevSh);
}
private SliceHeader readSliceHeader(ByteBuffer buf, NALUnit nu) {
BitReader br = BitReader.createBitReader(buf);
SliceHeader sh = SliceHeaderReader.readPart1(br);
PictureParameterSet pp = this.pps.get(sh.picParameterSetId);
SliceHeaderReader.readPart2(sh, nu, this.sps.get(pp.seqParameterSetId), pp, br);
return sh;
}
private boolean sameFrame(NALUnit nu1, NALUnit nu2, SliceHeader sh1, SliceHeader sh2) {
if (sh1.picParameterSetId != sh2.picParameterSetId)
return false;
if (sh1.frameNum != sh2.frameNum)
return false;
SeqParameterSet sps = sh1.sps;
if (sps.picOrderCntType == 0 && sh1.picOrderCntLsb != sh2.picOrderCntLsb)
return false;
if (sps.picOrderCntType == 1 && (sh1.deltaPicOrderCnt[0] != sh2.deltaPicOrderCnt[0] || sh1.deltaPicOrderCnt[1] != sh2.deltaPicOrderCnt[1]))
return false;
if ((nu1.nal_ref_idc == 0 || nu2.nal_ref_idc == 0) && nu1.nal_ref_idc != nu2.nal_ref_idc)
return false;
if (((nu1.type == NALUnitType.IDR_SLICE) ? true : false) != ((nu2.type == NALUnitType.IDR_SLICE) ? true : false))
return false;
if (sh1.idrPicId != sh2.idrPicId)
return false;
return true;
}
private Packet detectPoc(ByteBuffer result, NALUnit nu, SliceHeader sh) {
int maxFrameNum = 1 << sh.sps.log2MaxFrameNumMinus4 + 4;
if (detectGap(sh, maxFrameNum))
issueNonExistingPic(sh, maxFrameNum);
int absFrameNum = updateFrameNumber(sh.frameNum, maxFrameNum, detectMMCO5(sh.refPicMarkingNonIDR));
int poc = 0;
if (nu.type == NALUnitType.NON_IDR_SLICE)
poc = calcPoc(absFrameNum, nu, sh);
return new Packet(result, (long)absFrameNum, 1, 1L, (long)this.frameNo++, (nu.type == NALUnitType.IDR_SLICE) ? Packet.FrameType.KEY : Packet.FrameType.INTER, null, poc);
}
private int updateFrameNumber(int frameNo, int maxFrameNum, boolean mmco5) {
int frameNumOffset;
if (this.prevFrameNum > frameNo) {
frameNumOffset = this.prevFrameNumOffset + maxFrameNum;
} else {
frameNumOffset = this.prevFrameNumOffset;
}
int absFrameNum = frameNumOffset + frameNo;
this.prevFrameNum = mmco5 ? 0 : frameNo;
this.prevFrameNumOffset = frameNumOffset;
return absFrameNum;
}
private void issueNonExistingPic(SliceHeader sh, int maxFrameNum) {
int nextFrameNum = (this.prevFrameNum + 1) % maxFrameNum;
this.prevFrameNum = nextFrameNum;
}
private boolean detectGap(SliceHeader sh, int maxFrameNum) {
return (sh.frameNum != this.prevFrameNum && sh.frameNum != (this.prevFrameNum + 1) % maxFrameNum);
}
private int calcPoc(int absFrameNum, NALUnit nu, SliceHeader sh) {
if (sh.sps.picOrderCntType == 0)
return calcPOC0(nu, sh);
if (sh.sps.picOrderCntType == 1)
return calcPOC1(absFrameNum, nu, sh);
return calcPOC2(absFrameNum, nu, sh);
}
private int calcPOC2(int absFrameNum, NALUnit nu, SliceHeader sh) {
if (nu.nal_ref_idc == 0)
return 2 * absFrameNum - 1;
return 2 * absFrameNum;
}
private int calcPOC1(int absFrameNum, NALUnit nu, SliceHeader sh) {
int expectedPicOrderCnt;
if (sh.sps.numRefFramesInPicOrderCntCycle == 0)
absFrameNum = 0;
if (nu.nal_ref_idc == 0 && absFrameNum > 0)
absFrameNum--;
int expectedDeltaPerPicOrderCntCycle = 0;
for (int i = 0; i < sh.sps.numRefFramesInPicOrderCntCycle; i++)
expectedDeltaPerPicOrderCntCycle += sh.sps.offsetForRefFrame[i];
if (absFrameNum > 0) {
int picOrderCntCycleCnt = (absFrameNum - 1) / sh.sps.numRefFramesInPicOrderCntCycle;
int frameNumInPicOrderCntCycle = (absFrameNum - 1) % sh.sps.numRefFramesInPicOrderCntCycle;
expectedPicOrderCnt = picOrderCntCycleCnt * expectedDeltaPerPicOrderCntCycle;
for (int j = 0; j <= frameNumInPicOrderCntCycle; j++)
expectedPicOrderCnt += sh.sps.offsetForRefFrame[j];
} else {
expectedPicOrderCnt = 0;
}
if (nu.nal_ref_idc == 0)
expectedPicOrderCnt += sh.sps.offsetForNonRefPic;
return expectedPicOrderCnt + sh.deltaPicOrderCnt[0];
}
private int calcPOC0(NALUnit nu, SliceHeader sh) {
int picOrderCntMsb;
int pocCntLsb = sh.picOrderCntLsb;
int maxPicOrderCntLsb = 1 << sh.sps.log2MaxPicOrderCntLsbMinus4 + 4;
if (pocCntLsb < this.prevPicOrderCntLsb && this.prevPicOrderCntLsb - pocCntLsb >= maxPicOrderCntLsb / 2) {
picOrderCntMsb = this.prevPicOrderCntMsb + maxPicOrderCntLsb;
} else if (pocCntLsb > this.prevPicOrderCntLsb && pocCntLsb - this.prevPicOrderCntLsb > maxPicOrderCntLsb / 2) {
picOrderCntMsb = this.prevPicOrderCntMsb - maxPicOrderCntLsb;
} else {
picOrderCntMsb = this.prevPicOrderCntMsb;
}
if (nu.nal_ref_idc != 0) {
this.prevPicOrderCntMsb = picOrderCntMsb;
this.prevPicOrderCntLsb = pocCntLsb;
}
return picOrderCntMsb + pocCntLsb;
}
private boolean detectMMCO5(RefPicMarking refPicMarkingNonIDR) {
if (refPicMarkingNonIDR == null)
return false;
RefPicMarking.Instruction[] instructions = refPicMarkingNonIDR.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction instr = instructions[i];
if (instr.getType() == RefPicMarking.InstrType.CLEAR)
return true;
}
return false;
}
public SeqParameterSet[] getSps() {
return this.sps.values(new SeqParameterSet[0]);
}
public PictureParameterSet[] getPps() {
return this.pps.values(new PictureParameterSet[0]);
}
public DemuxerTrackMeta getMeta() {
return null;
}
public void close() throws IOException {}
public List<? extends DemuxerTrack> getTracks() {
return getVideoTracks();
}
public List<? extends DemuxerTrack> getVideoTracks() {
List<DemuxerTrack> tracks = new ArrayList<>();
tracks.add(this);
return tracks;
}
public List<? extends DemuxerTrack> getAudioTracks() {
List<DemuxerTrack> tracks = new ArrayList<>();
return tracks;
}
}

View file

@ -0,0 +1,694 @@
package org.jcodec.codecs.h264;
import java.util.Arrays;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.common.io.VLC;
import org.jcodec.common.io.VLCBuilder;
import org.jcodec.common.model.Picture;
public class H264Const {
public static final VLC[] CoeffToken = new VLC[10];
public static final VLC coeffTokenChromaDCY420;
public static final VLC coeffTokenChromaDCY422;
static {
VLCBuilder vbl = new VLCBuilder();
vbl.set(0, "1");
vbl.set(coeffToken(1, 0), "000101");
vbl.set(coeffToken(1, 1), "01");
vbl.set(coeffToken(2, 0), "00000111");
vbl.set(coeffToken(2, 1), "000100");
vbl.set(coeffToken(2, 2), "001");
vbl.set(coeffToken(3, 0), "000000111");
vbl.set(coeffToken(3, 1), "00000110");
vbl.set(coeffToken(3, 2), "0000101");
vbl.set(coeffToken(3, 3), "00011");
vbl.set(coeffToken(4, 0), "0000000111");
vbl.set(coeffToken(4, 1), "000000110");
vbl.set(coeffToken(4, 2), "00000101");
vbl.set(coeffToken(4, 3), "000011");
vbl.set(coeffToken(5, 0), "00000000111");
vbl.set(coeffToken(5, 1), "0000000110");
vbl.set(coeffToken(5, 2), "000000101");
vbl.set(coeffToken(5, 3), "0000100");
vbl.set(coeffToken(6, 0), "0000000001111");
vbl.set(coeffToken(6, 1), "00000000110");
vbl.set(coeffToken(6, 2), "0000000101");
vbl.set(coeffToken(6, 3), "00000100");
vbl.set(coeffToken(7, 0), "0000000001011");
vbl.set(coeffToken(7, 1), "0000000001110");
vbl.set(coeffToken(7, 2), "00000000101");
vbl.set(coeffToken(7, 3), "000000100");
vbl.set(coeffToken(8, 0), "0000000001000");
vbl.set(coeffToken(8, 1), "0000000001010");
vbl.set(coeffToken(8, 2), "0000000001101");
vbl.set(coeffToken(8, 3), "0000000100");
vbl.set(coeffToken(9, 0), "00000000001111");
vbl.set(coeffToken(9, 1), "00000000001110");
vbl.set(coeffToken(9, 2), "0000000001001");
vbl.set(coeffToken(9, 3), "00000000100");
vbl.set(coeffToken(10, 0), "00000000001011");
vbl.set(coeffToken(10, 1), "00000000001010");
vbl.set(coeffToken(10, 2), "00000000001101");
vbl.set(coeffToken(10, 3), "0000000001100");
vbl.set(coeffToken(11, 0), "000000000001111");
vbl.set(coeffToken(11, 1), "000000000001110");
vbl.set(coeffToken(11, 2), "00000000001001");
vbl.set(coeffToken(11, 3), "00000000001100");
vbl.set(coeffToken(12, 0), "000000000001011");
vbl.set(coeffToken(12, 1), "000000000001010");
vbl.set(coeffToken(12, 2), "000000000001101");
vbl.set(coeffToken(12, 3), "00000000001000");
vbl.set(coeffToken(13, 0), "0000000000001111");
vbl.set(coeffToken(13, 1), "000000000000001");
vbl.set(coeffToken(13, 2), "000000000001001");
vbl.set(coeffToken(13, 3), "000000000001100");
vbl.set(coeffToken(14, 0), "0000000000001011");
vbl.set(coeffToken(14, 1), "0000000000001110");
vbl.set(coeffToken(14, 2), "0000000000001101");
vbl.set(coeffToken(14, 3), "000000000001000");
vbl.set(coeffToken(15, 0), "0000000000000111");
vbl.set(coeffToken(15, 1), "0000000000001010");
vbl.set(coeffToken(15, 2), "0000000000001001");
vbl.set(coeffToken(15, 3), "0000000000001100");
vbl.set(coeffToken(16, 0), "0000000000000100");
vbl.set(coeffToken(16, 1), "0000000000000110");
vbl.set(coeffToken(16, 2), "0000000000000101");
vbl.set(coeffToken(16, 3), "0000000000001000");
CoeffToken[0] = CoeffToken[1] = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "11");
vbl.set(coeffToken(1, 0), "001011");
vbl.set(coeffToken(1, 1), "10");
vbl.set(coeffToken(2, 0), "000111");
vbl.set(coeffToken(2, 1), "00111");
vbl.set(coeffToken(2, 2), "011");
vbl.set(coeffToken(3, 0), "0000111");
vbl.set(coeffToken(3, 1), "001010");
vbl.set(coeffToken(3, 2), "001001");
vbl.set(coeffToken(3, 3), "0101");
vbl.set(coeffToken(4, 0), "00000111");
vbl.set(coeffToken(4, 1), "000110");
vbl.set(coeffToken(4, 2), "000101");
vbl.set(coeffToken(4, 3), "0100");
vbl.set(coeffToken(5, 0), "00000100");
vbl.set(coeffToken(5, 1), "0000110");
vbl.set(coeffToken(5, 2), "0000101");
vbl.set(coeffToken(5, 3), "00110");
vbl.set(coeffToken(6, 0), "000000111");
vbl.set(coeffToken(6, 1), "00000110");
vbl.set(coeffToken(6, 2), "00000101");
vbl.set(coeffToken(6, 3), "001000");
vbl.set(coeffToken(7, 0), "00000001111");
vbl.set(coeffToken(7, 1), "000000110");
vbl.set(coeffToken(7, 2), "000000101");
vbl.set(coeffToken(7, 3), "000100");
vbl.set(coeffToken(8, 0), "00000001011");
vbl.set(coeffToken(8, 1), "00000001110");
vbl.set(coeffToken(8, 2), "00000001101");
vbl.set(coeffToken(8, 3), "0000100");
vbl.set(coeffToken(9, 0), "000000001111");
vbl.set(coeffToken(9, 1), "00000001010");
vbl.set(coeffToken(9, 2), "00000001001");
vbl.set(coeffToken(9, 3), "000000100");
vbl.set(coeffToken(10, 0), "000000001011");
vbl.set(coeffToken(10, 1), "000000001110");
vbl.set(coeffToken(10, 2), "000000001101");
vbl.set(coeffToken(10, 3), "00000001100");
vbl.set(coeffToken(11, 0), "000000001000");
vbl.set(coeffToken(11, 1), "000000001010");
vbl.set(coeffToken(11, 2), "000000001001");
vbl.set(coeffToken(11, 3), "00000001000");
vbl.set(coeffToken(12, 0), "0000000001111");
vbl.set(coeffToken(12, 1), "0000000001110");
vbl.set(coeffToken(12, 2), "0000000001101");
vbl.set(coeffToken(12, 3), "000000001100");
vbl.set(coeffToken(13, 0), "0000000001011");
vbl.set(coeffToken(13, 1), "0000000001010");
vbl.set(coeffToken(13, 2), "0000000001001");
vbl.set(coeffToken(13, 3), "0000000001100");
vbl.set(coeffToken(14, 0), "0000000000111");
vbl.set(coeffToken(14, 1), "00000000001011");
vbl.set(coeffToken(14, 2), "0000000000110");
vbl.set(coeffToken(14, 3), "0000000001000");
vbl.set(coeffToken(15, 0), "00000000001001");
vbl.set(coeffToken(15, 1), "00000000001000");
vbl.set(coeffToken(15, 2), "00000000001010");
vbl.set(coeffToken(15, 3), "0000000000001");
vbl.set(coeffToken(16, 0), "00000000000111");
vbl.set(coeffToken(16, 1), "00000000000110");
vbl.set(coeffToken(16, 2), "00000000000101");
vbl.set(coeffToken(16, 3), "00000000000100");
CoeffToken[2] = CoeffToken[3] = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "1111");
vbl.set(coeffToken(1, 0), "001111");
vbl.set(coeffToken(1, 1), "1110");
vbl.set(coeffToken(2, 0), "001011");
vbl.set(coeffToken(2, 1), "01111");
vbl.set(coeffToken(2, 2), "1101");
vbl.set(coeffToken(3, 0), "001000");
vbl.set(coeffToken(3, 1), "01100");
vbl.set(coeffToken(3, 2), "01110");
vbl.set(coeffToken(3, 3), "1100");
vbl.set(coeffToken(4, 0), "0001111");
vbl.set(coeffToken(4, 1), "01010");
vbl.set(coeffToken(4, 2), "01011");
vbl.set(coeffToken(4, 3), "1011");
vbl.set(coeffToken(5, 0), "0001011");
vbl.set(coeffToken(5, 1), "01000");
vbl.set(coeffToken(5, 2), "01001");
vbl.set(coeffToken(5, 3), "1010");
vbl.set(coeffToken(6, 0), "0001001");
vbl.set(coeffToken(6, 1), "001110");
vbl.set(coeffToken(6, 2), "001101");
vbl.set(coeffToken(6, 3), "1001");
vbl.set(coeffToken(7, 0), "0001000");
vbl.set(coeffToken(7, 1), "001010");
vbl.set(coeffToken(7, 2), "001001");
vbl.set(coeffToken(7, 3), "1000");
vbl.set(coeffToken(8, 0), "00001111");
vbl.set(coeffToken(8, 1), "0001110");
vbl.set(coeffToken(8, 2), "0001101");
vbl.set(coeffToken(8, 3), "01101");
vbl.set(coeffToken(9, 0), "00001011");
vbl.set(coeffToken(9, 1), "00001110");
vbl.set(coeffToken(9, 2), "0001010");
vbl.set(coeffToken(9, 3), "001100");
vbl.set(coeffToken(10, 0), "000001111");
vbl.set(coeffToken(10, 1), "00001010");
vbl.set(coeffToken(10, 2), "00001101");
vbl.set(coeffToken(10, 3), "0001100");
vbl.set(coeffToken(11, 0), "000001011");
vbl.set(coeffToken(11, 1), "000001110");
vbl.set(coeffToken(11, 2), "00001001");
vbl.set(coeffToken(11, 3), "00001100");
vbl.set(coeffToken(12, 0), "000001000");
vbl.set(coeffToken(12, 1), "000001010");
vbl.set(coeffToken(12, 2), "000001101");
vbl.set(coeffToken(12, 3), "00001000");
vbl.set(coeffToken(13, 0), "0000001101");
vbl.set(coeffToken(13, 1), "000000111");
vbl.set(coeffToken(13, 2), "000001001");
vbl.set(coeffToken(13, 3), "000001100");
vbl.set(coeffToken(14, 0), "0000001001");
vbl.set(coeffToken(14, 1), "0000001100");
vbl.set(coeffToken(14, 2), "0000001011");
vbl.set(coeffToken(14, 3), "0000001010");
vbl.set(coeffToken(15, 0), "0000000101");
vbl.set(coeffToken(15, 1), "0000001000");
vbl.set(coeffToken(15, 2), "0000000111");
vbl.set(coeffToken(15, 3), "0000000110");
vbl.set(coeffToken(16, 0), "0000000001");
vbl.set(coeffToken(16, 1), "0000000100");
vbl.set(coeffToken(16, 2), "0000000011");
vbl.set(coeffToken(16, 3), "0000000010");
CoeffToken[4] = CoeffToken[5] = CoeffToken[6] = CoeffToken[7] = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "000011");
vbl.set(coeffToken(1, 0), "000000");
vbl.set(coeffToken(1, 1), "000001");
vbl.set(coeffToken(2, 0), "000100");
vbl.set(coeffToken(2, 1), "000101");
vbl.set(coeffToken(2, 2), "000110");
vbl.set(coeffToken(3, 0), "001000");
vbl.set(coeffToken(3, 1), "001001");
vbl.set(coeffToken(3, 2), "001010");
vbl.set(coeffToken(3, 3), "001011");
vbl.set(coeffToken(4, 0), "001100");
vbl.set(coeffToken(4, 1), "001101");
vbl.set(coeffToken(4, 2), "001110");
vbl.set(coeffToken(4, 3), "001111");
vbl.set(coeffToken(5, 0), "010000");
vbl.set(coeffToken(5, 1), "010001");
vbl.set(coeffToken(5, 2), "010010");
vbl.set(coeffToken(5, 3), "010011");
vbl.set(coeffToken(6, 0), "010100");
vbl.set(coeffToken(6, 1), "010101");
vbl.set(coeffToken(6, 2), "010110");
vbl.set(coeffToken(6, 3), "010111");
vbl.set(coeffToken(7, 0), "011000");
vbl.set(coeffToken(7, 1), "011001");
vbl.set(coeffToken(7, 2), "011010");
vbl.set(coeffToken(7, 3), "011011");
vbl.set(coeffToken(8, 0), "011100");
vbl.set(coeffToken(8, 1), "011101");
vbl.set(coeffToken(8, 2), "011110");
vbl.set(coeffToken(8, 3), "011111");
vbl.set(coeffToken(9, 0), "100000");
vbl.set(coeffToken(9, 1), "100001");
vbl.set(coeffToken(9, 2), "100010");
vbl.set(coeffToken(9, 3), "100011");
vbl.set(coeffToken(10, 0), "100100");
vbl.set(coeffToken(10, 1), "100101");
vbl.set(coeffToken(10, 2), "100110");
vbl.set(coeffToken(10, 3), "100111");
vbl.set(coeffToken(11, 0), "101000");
vbl.set(coeffToken(11, 1), "101001");
vbl.set(coeffToken(11, 2), "101010");
vbl.set(coeffToken(11, 3), "101011");
vbl.set(coeffToken(12, 0), "101100");
vbl.set(coeffToken(12, 1), "101101");
vbl.set(coeffToken(12, 2), "101110");
vbl.set(coeffToken(12, 3), "101111");
vbl.set(coeffToken(13, 0), "110000");
vbl.set(coeffToken(13, 1), "110001");
vbl.set(coeffToken(13, 2), "110010");
vbl.set(coeffToken(13, 3), "110011");
vbl.set(coeffToken(14, 0), "110100");
vbl.set(coeffToken(14, 1), "110101");
vbl.set(coeffToken(14, 2), "110110");
vbl.set(coeffToken(14, 3), "110111");
vbl.set(coeffToken(15, 0), "111000");
vbl.set(coeffToken(15, 1), "111001");
vbl.set(coeffToken(15, 2), "111010");
vbl.set(coeffToken(15, 3), "111011");
vbl.set(coeffToken(16, 0), "111100");
vbl.set(coeffToken(16, 1), "111101");
vbl.set(coeffToken(16, 2), "111110");
vbl.set(coeffToken(16, 3), "111111");
CoeffToken[8] = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "01");
vbl.set(coeffToken(1, 0), "000111");
vbl.set(coeffToken(1, 1), "1");
vbl.set(coeffToken(2, 0), "000100");
vbl.set(coeffToken(2, 1), "000110");
vbl.set(coeffToken(2, 2), "001");
vbl.set(coeffToken(3, 0), "000011");
vbl.set(coeffToken(3, 1), "0000011");
vbl.set(coeffToken(3, 2), "0000010");
vbl.set(coeffToken(3, 3), "000101");
vbl.set(coeffToken(4, 0), "000010");
vbl.set(coeffToken(4, 1), "00000011");
vbl.set(coeffToken(4, 2), "00000010");
vbl.set(coeffToken(4, 3), "0000000");
coeffTokenChromaDCY420 = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "1");
vbl.set(coeffToken(1, 0), "0001111");
vbl.set(coeffToken(1, 1), "01");
vbl.set(coeffToken(2, 0), "0001110");
vbl.set(coeffToken(2, 1), "0001101");
vbl.set(coeffToken(2, 2), "001");
vbl.set(coeffToken(3, 0), "000000111");
vbl.set(coeffToken(3, 1), "0001100");
vbl.set(coeffToken(3, 2), "0001011");
vbl.set(coeffToken(3, 3), "00001");
vbl.set(coeffToken(4, 0), "000000110");
vbl.set(coeffToken(4, 1), "000000101");
vbl.set(coeffToken(4, 2), "0001010");
vbl.set(coeffToken(4, 3), "000001");
vbl.set(coeffToken(5, 0), "0000000111");
vbl.set(coeffToken(5, 1), "0000000110");
vbl.set(coeffToken(5, 2), "000000100");
vbl.set(coeffToken(5, 3), "0001001");
vbl.set(coeffToken(6, 0), "00000000111");
vbl.set(coeffToken(6, 1), "00000000110");
vbl.set(coeffToken(6, 2), "0000000101");
vbl.set(coeffToken(6, 3), "0001000");
vbl.set(coeffToken(7, 0), "000000000111");
vbl.set(coeffToken(7, 1), "000000000110");
vbl.set(coeffToken(7, 2), "00000000101");
vbl.set(coeffToken(7, 3), "0000000100");
vbl.set(coeffToken(8, 0), "0000000000111");
vbl.set(coeffToken(8, 1), "000000000101");
vbl.set(coeffToken(8, 2), "000000000100");
vbl.set(coeffToken(8, 3), "00000000100");
coeffTokenChromaDCY422 = vbl.getVLC();
}
public static final VLC[] run = new VLC[] { new VLCBuilder()
.set(0, "1").set(1, "0").getVLC(), new VLCBuilder()
.set(0, "1").set(1, "01").set(2, "00").getVLC(), new VLCBuilder()
.set(0, "11").set(1, "10").set(2, "01").set(3, "00").getVLC(), new VLCBuilder()
.set(0, "11").set(1, "10").set(2, "01").set(3, "001").set(4, "000").getVLC(), new VLCBuilder()
.set(0, "11").set(1, "10").set(2, "011").set(3, "010").set(4, "001").set(5, "000")
.getVLC(), new VLCBuilder()
.set(0, "11").set(1, "000").set(2, "001").set(3, "011").set(4, "010").set(5, "101")
.set(6, "100").getVLC(), new VLCBuilder()
.set(0, "111").set(1, "110").set(2, "101").set(3, "100").set(4, "011").set(5, "010")
.set(6, "001").set(7, "0001").set(8, "00001").set(9, "000001").set(10, "0000001")
.set(11, "00000001").set(12, "000000001").set(13, "0000000001").set(14, "00000000001").getVLC() };
public static final VLC[] totalZeros16 = new VLC[] {
new VLCBuilder()
.set(0, "1").set(1, "011").set(2, "010").set(3, "0011").set(4, "0010").set(5, "00011")
.set(6, "00010").set(7, "000011").set(8, "000010").set(9, "0000011").set(10, "0000010")
.set(11, "00000011").set(12, "00000010").set(13, "000000011").set(14, "000000010")
.set(15, "000000001").getVLC(), new VLCBuilder()
.set(0, "111").set(1, "110").set(2, "101").set(3, "100").set(4, "011").set(5, "0101")
.set(6, "0100").set(7, "0011").set(8, "0010").set(9, "00011").set(10, "00010").set(11, "000011")
.set(12, "000010").set(13, "000001").set(14, "000000").getVLC(), new VLCBuilder()
.set(0, "0101").set(1, "111").set(2, "110").set(3, "101").set(4, "0100").set(5, "0011")
.set(6, "100").set(7, "011").set(8, "0010").set(9, "00011").set(10, "00010").set(11, "000001")
.set(12, "00001").set(13, "000000").getVLC(), new VLCBuilder()
.set(0, "00011").set(1, "111").set(2, "0101").set(3, "0100").set(4, "110").set(5, "101")
.set(6, "100").set(7, "0011").set(8, "011").set(9, "0010").set(10, "00010").set(11, "00001")
.set(12, "00000").getVLC(), new VLCBuilder()
.set(0, "0101").set(1, "0100").set(2, "0011").set(3, "111").set(4, "110").set(5, "101")
.set(6, "100").set(7, "011").set(8, "0010").set(9, "00001").set(10, "0001").set(11, "00000")
.getVLC(), new VLCBuilder()
.set(0, "000001").set(1, "00001").set(2, "111").set(3, "110").set(4, "101").set(5, "100")
.set(6, "011").set(7, "010").set(8, "0001").set(9, "001").set(10, "000000").getVLC(), new VLCBuilder()
.set(0, "000001").set(1, "00001").set(2, "101").set(3, "100").set(4, "011").set(5, "11")
.set(6, "010").set(7, "0001").set(8, "001").set(9, "000000").getVLC(), new VLCBuilder()
.set(0, "000001").set(1, "0001").set(2, "00001").set(3, "011").set(4, "11").set(5, "10")
.set(6, "010").set(7, "001").set(8, "000000").getVLC(), new VLCBuilder()
.set(0, "000001").set(1, "000000").set(2, "0001").set(3, "11").set(4, "10").set(5, "001")
.set(6, "01").set(7, "00001").getVLC(), new VLCBuilder()
.set(0, "00001").set(1, "00000").set(2, "001").set(3, "11").set(4, "10").set(5, "01")
.set(6, "0001").getVLC(),
new VLCBuilder()
.set(0, "0000").set(1, "0001").set(2, "001").set(3, "010").set(4, "1").set(5, "011")
.getVLC(), new VLCBuilder()
.set(0, "0000").set(1, "0001").set(2, "01").set(3, "1").set(4, "001").getVLC(), new VLCBuilder()
.set(0, "000").set(1, "001").set(2, "1").set(3, "01").getVLC(), new VLCBuilder()
.set(0, "00").set(1, "01").set(2, "1").getVLC(), new VLCBuilder()
.set(0, "0").set(1, "1").getVLC() };
public static final VLC[] totalZeros4 = new VLC[] { new VLCBuilder().set(0, "1").set(1, "01").set(2, "001").set(3, "000").getVLC(), new VLCBuilder()
.set(0, "1").set(1, "01").set(2, "00").getVLC(), new VLCBuilder()
.set(0, "1").set(1, "0").getVLC() };
public static final VLC[] totalZeros8 = new VLC[] { new VLCBuilder()
.set(0, "1").set(1, "010").set(2, "011").set(3, "0010").set(4, "0011").set(5, "0001")
.set(6, "00001").set(7, "00000").getVLC(), new VLCBuilder()
.set(0, "000").set(1, "01").set(2, "001").set(3, "100").set(4, "101").set(5, "110")
.set(6, "111").getVLC(), new VLCBuilder()
.set(0, "000").set(1, "001").set(2, "01").set(3, "10").set(4, "110").set(5, "111").getVLC(), new VLCBuilder()
.set(0, "110").set(1, "00").set(2, "01").set(3, "10").set(4, "111").getVLC(), new VLCBuilder()
.set(0, "00").set(1, "01").set(2, "10").set(3, "11").getVLC(), new VLCBuilder()
.set(0, "00").set(1, "01").set(2, "1").getVLC(), new VLCBuilder()
.set(0, "0").set(1, "1").getVLC() };
public enum PartPred {
L0, L1, Bi, Direct;
}
public static final PartPred[][] bPredModes = new PartPred[][] {
null, new PartPred[] { PartPred.L0 }, new PartPred[] { PartPred.L1 }, new PartPred[] { PartPred.Bi }, new PartPred[] { PartPred.L0, PartPred.L0 }, new PartPred[] { PartPred.L0, PartPred.L0 }, new PartPred[] { PartPred.L1, PartPred.L1 }, new PartPred[] { PartPred.L1, PartPred.L1 }, new PartPred[] { PartPred.L0, PartPred.L1 }, new PartPred[] { PartPred.L0, PartPred.L1 },
new PartPred[] { PartPred.L1, PartPred.L0 }, new PartPred[] { PartPred.L1, PartPred.L0 }, new PartPred[] { PartPred.L0, PartPred.Bi }, new PartPred[] { PartPred.L0, PartPred.Bi }, new PartPred[] { PartPred.L1, PartPred.Bi }, new PartPred[] { PartPred.L1, PartPred.Bi }, new PartPred[] { PartPred.Bi, PartPred.L0 }, new PartPred[] { PartPred.Bi, PartPred.L0 }, new PartPred[] { PartPred.Bi, PartPred.L1 }, new PartPred[] { PartPred.Bi, PartPred.L1 },
new PartPred[] { PartPred.Bi, PartPred.Bi }, new PartPred[] { PartPred.Bi, PartPred.Bi } };
public static final MBType[] bMbTypes = new MBType[] {
MBType.B_Direct_16x16, MBType.B_L0_16x16, MBType.B_L1_16x16, MBType.B_Bi_16x16, MBType.B_L0_L0_16x8, MBType.B_L0_L0_8x16, MBType.B_L1_L1_16x8, MBType.B_L1_L1_8x16, MBType.B_L0_L1_16x8, MBType.B_L0_L1_8x16,
MBType.B_L1_L0_16x8, MBType.B_L1_L0_8x16, MBType.B_L0_Bi_16x8, MBType.B_L0_Bi_8x16, MBType.B_L1_Bi_16x8, MBType.B_L1_Bi_8x16, MBType.B_Bi_L0_16x8, MBType.B_Bi_L0_8x16, MBType.B_Bi_L1_16x8, MBType.B_Bi_L1_8x16,
MBType.B_Bi_Bi_16x8, MBType.B_Bi_Bi_8x16, MBType.B_8x8 };
public static final int[] bPartW = new int[] {
0, 16, 16, 16, 16, 8, 16, 8, 16, 8,
16, 8, 16, 8, 16, 8, 16, 8, 16, 8,
16, 8 };
public static final int[] bPartH = new int[] {
0, 16, 16, 16, 8, 16, 8, 16, 8, 16,
8, 16, 8, 16, 8, 16, 8, 16, 8, 16,
8, 16 };
public static final int[] BLK_X = new int[] {
0, 4, 0, 4, 8, 12, 8, 12, 0, 4,
0, 4, 8, 12, 8, 12 };
public static final int[] BLK_Y = new int[] {
0, 0, 4, 4, 0, 0, 4, 4, 8, 8,
12, 12, 8, 8, 12, 12 };
public static final int[] BLK_8x8_X = new int[] { 0, 8, 0, 8 };
public static final int[] BLK_8x8_Y = new int[] { 0, 0, 8, 8 };
public static final int[] BLK_INV_MAP = new int[] {
0, 1, 4, 5, 2, 3, 6, 7, 8, 9,
12, 13, 10, 11, 14, 15 };
public static final int[] MB_BLK_OFF_LEFT = new int[] {
0, 1, 0, 1, 2, 3, 2, 3, 0, 1,
0, 1, 2, 3, 2, 3 };
public static final int[] MB_BLK_OFF_TOP = new int[] {
0, 0, 1, 1, 0, 0, 1, 1, 2, 2,
3, 3, 2, 2, 3, 3 };
public static final int[] QP_SCALE_CR = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
29, 30, 31, 32, 32, 33, 34, 34, 35, 35,
36, 36, 37, 37, 37, 38, 38, 38, 39, 39,
39, 39 };
public static final Picture NO_PIC = Picture.createPicture(0, 0, null, null);
public static final int[] BLK_8x8_MB_OFF_LUMA = new int[] { 0, 8, 128, 136 };
public static final int[] BLK_8x8_MB_OFF_CHROMA = new int[] { 0, 4, 32, 36 };
public static final int[] BLK_4x4_MB_OFF_LUMA = new int[] {
0, 4, 8, 12, 64, 68, 72, 76, 128, 132,
136, 140, 192, 196, 200, 204 };
public static final int[] BLK_8x8_IND = new int[] {
0, 0, 1, 1, 0, 0, 1, 1, 2, 2,
3, 3, 2, 2, 3, 3 };
public static final int[][] BLK8x8_BLOCKS = new int[][] { new int[] { 0, 1, 4, 5 }, new int[] { 2, 3, 6, 7 }, new int[] { 8, 9, 12, 13 }, new int[] { 10, 11, 14, 15 } };
public static final int[][] ARRAY = new int[][] { new int[] { 0 }, new int[] { 1 }, new int[] { 2 }, new int[] { 3 } };
public static final int[] CODED_BLOCK_PATTERN_INTRA_COLOR = new int[] {
47, 31, 15, 0, 23, 27, 29, 30, 7, 11,
13, 14, 39, 43, 45, 46, 16, 3, 5, 10,
12, 19, 21, 26, 28, 35, 37, 42, 44, 1,
2, 4, 8, 17, 18, 20, 24, 6, 9, 22,
25, 32, 33, 34, 36, 40, 38, 41 };
public static final int[] coded_block_pattern_intra_monochrome = new int[] {
15, 0, 7, 11, 13, 14, 3, 5, 10, 12,
1, 2, 4, 8, 6, 9 };
public static final int[] CODED_BLOCK_PATTERN_INTER_COLOR = new int[] {
0, 16, 1, 2, 4, 8, 32, 3, 5, 10,
12, 15, 47, 7, 11, 13, 14, 6, 9, 31,
35, 37, 42, 44, 33, 34, 36, 40, 39, 43,
45, 46, 17, 18, 20, 24, 19, 21, 26, 28,
23, 27, 29, 30, 22, 25, 38, 41 };
private static int[] inverse(int[] arr) {
int[] inv = new int[arr.length];
for (int i = 0; i < inv.length; i++)
inv[arr[i]] = i;
return inv;
}
public static final int[] CODED_BLOCK_PATTERN_INTER_COLOR_INV = inverse(CODED_BLOCK_PATTERN_INTER_COLOR);
public static final int[] coded_block_pattern_inter_monochrome = new int[] {
0, 1, 2, 4, 8, 3, 5, 10, 12, 15,
7, 11, 13, 14, 6, 9 };
public static final int[] sig_coeff_map_8x8 = new int[] {
0, 1, 2, 3, 4, 5, 5, 4, 4, 3,
3, 4, 4, 4, 5, 5, 4, 4, 4, 4,
3, 3, 6, 7, 7, 7, 8, 9, 10, 9,
8, 7, 7, 6, 11, 12, 13, 11, 6, 7,
8, 9, 14, 10, 9, 8, 6, 11, 12, 13,
11, 6, 9, 14, 10, 9, 11, 12, 13, 11,
14, 10, 12 };
public static final int[] sig_coeff_map_8x8_mbaff = new int[] {
0, 1, 1, 2, 2, 3, 3, 4, 5, 6,
7, 7, 7, 8, 4, 5, 6, 9, 10, 10,
8, 11, 12, 11, 9, 9, 10, 10, 8, 11,
12, 11, 9, 9, 10, 10, 8, 11, 12, 11,
9, 9, 10, 10, 8, 13, 13, 9, 9, 10,
10, 8, 13, 13, 9, 9, 10, 10, 14, 14,
14, 14, 14 };
public static final int[] last_sig_coeff_map_8x8 = new int[] {
0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 5, 5,
5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
8, 8, 8 };
public static final int[] identityMapping16 = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15 };
public static final int[] identityMapping4 = new int[] { 0, 1, 2, 3 };
public static final PartPred[] bPartPredModes = new PartPred[] {
PartPred.Direct, PartPred.L0, PartPred.L1, PartPred.Bi, PartPred.L0, PartPred.L0, PartPred.L1, PartPred.L1, PartPred.Bi, PartPred.Bi,
PartPred.L0, PartPred.L1, PartPred.Bi };
public static final int[] bSubMbTypes = new int[] {
0, 0, 0, 0, 1, 2, 1, 2, 1, 2,
3, 3, 3 };
public static final int[] LUMA_4x4_BLOCK_LUT = new int[256];
public static final int[] LUMA_4x4_POS_LUT = new int[256];
public static final int[] LUMA_8x8_BLOCK_LUT = new int[256];
public static final int[] LUMA_8x8_POS_LUT = new int[256];
public static final int[] CHROMA_BLOCK_LUT = new int[64];
public static final int[] CHROMA_POS_LUT = new int[64];
public static final int[][] COMP_BLOCK_4x4_LUT = new int[][] { LUMA_4x4_BLOCK_LUT, CHROMA_BLOCK_LUT, CHROMA_BLOCK_LUT };
public static final int[][] COMP_POS_4x4_LUT = new int[][] { LUMA_4x4_POS_LUT, CHROMA_POS_LUT, CHROMA_POS_LUT };
public static final int[][] COMP_BLOCK_8x8_LUT = new int[][] { LUMA_8x8_BLOCK_LUT, CHROMA_BLOCK_LUT, CHROMA_BLOCK_LUT };
public static final int[][] COMP_POS_8x8_LUT = new int[][] { LUMA_8x8_POS_LUT, CHROMA_POS_LUT, CHROMA_POS_LUT };
static {
int[] tmp = new int[16];
for (int j = 0; j < 16; j++) {
for (int k = 0; k < 16; k++)
tmp[k] = k;
putBlk(tmp, BLK_X[j], BLK_Y[j], 4, 4, 16, LUMA_4x4_POS_LUT);
Arrays.fill(tmp, j);
putBlk(tmp, BLK_X[j], BLK_Y[j], 4, 4, 16, LUMA_4x4_BLOCK_LUT);
}
for (int i = 0; i < 4; i++) {
for (int k = 0; k < 16; k++)
tmp[k] = k;
putBlk(tmp, BLK_X[i], BLK_Y[i], 4, 4, 8, CHROMA_POS_LUT);
Arrays.fill(tmp, i);
putBlk(tmp, BLK_X[i], BLK_Y[i], 4, 4, 8, CHROMA_BLOCK_LUT);
}
tmp = new int[64];
for (int blk = 0; blk < 4; blk++) {
for (int k = 0; k < 64; k++)
tmp[k] = k;
putBlk(tmp, BLK_8x8_X[blk], BLK_8x8_Y[blk], 8, 8, 16, LUMA_8x8_POS_LUT);
Arrays.fill(tmp, blk);
putBlk(tmp, BLK_8x8_X[blk], BLK_8x8_Y[blk], 8, 8, 16, LUMA_8x8_BLOCK_LUT);
}
}
private static void putBlk(int[] _in, int blkX, int blkY, int blkW, int blkH, int stride, int[] out) {
for (int line = 0, srcOff = 0, dstOff = blkY * stride + blkX; line < blkH; line++) {
for (int i = 0; i < blkW; i++)
out[dstOff + i] = _in[srcOff + i];
srcOff += blkW;
dstOff += stride;
}
}
private static int[][] buildPixSplitMap4x4() {
int[][] result = {
new int[] {
0, 1, 2, 3, 16, 17, 18, 19, 32, 33,
34, 35, 48, 49, 50, 51 }, new int[16], new int[16], new int[16], new int[16], new int[16], new int[16], new int[16], new int[16], new int[16],
new int[16], new int[16], new int[16], new int[16], new int[16], new int[16] };
for (int blkY = 0, blk = 0, off = 0; blkY < 4; blkY++) {
for (int blkX = 0; blkX < 4; blkX++, blk++, off += 4) {
for (int i = 0; i < 16; i++)
result[blk][i] = result[0][i] + off;
}
off += 48;
}
return result;
}
private static int[][] buildPixSplitMap2x2() {
int[][] result = { new int[] {
0, 1, 2, 3, 8, 9, 10, 11, 16, 17,
18, 19, 24, 25, 26, 27 }, new int[16], new int[16], new int[16] };
for (int blkY = 0, blk = 0, off = 0; blkY < 2; blkY++) {
for (int blkX = 0; blkX < 2; blkX++, blk++, off += 4) {
for (int i = 0; i < 16; i++)
result[blk][i] = result[0][i] + off;
}
off += 24;
}
return result;
}
public static boolean usesList(PartPred pred, int l) {
return (pred == PartPred.Bi) ? true : (((pred == PartPred.L0 && l == 0) || (pred == PartPred.L1 && l == 1)));
}
public static final int coeffToken(int totalCoeff, int trailingOnes) {
return totalCoeff << 4 | trailingOnes;
}
public static final int[][] PIX_MAP_SPLIT_4x4 = buildPixSplitMap4x4();
public static final int[][] PIX_MAP_SPLIT_2x2 = buildPixSplitMap2x2();
public static final int PROFILE_CAVLC_INTRA = 44;
public static final int PROFILE_BASELINE = 66;
public static final int PROFILE_MAIN = 77;
public static final int PROFILE_EXTENDED = 88;
public static final int PROFILE_HIGH = 100;
public static final int PROFILE_HIGH_10 = 110;
public static final int PROFILE_HIGH_422 = 122;
public static final int PROFILE_HIGH_444 = 244;
public static final int[] defaultScalingList4x4Intra = new int[] {
6, 13, 13, 20, 20, 20, 28, 28, 28, 28,
32, 32, 32, 37, 37, 42 };
public static final int[] defaultScalingList4x4Inter = new int[] {
10, 14, 14, 20, 20, 20, 24, 24, 24, 24,
27, 27, 27, 30, 30, 34 };
public static final int[] defaultScalingList8x8Intra = new int[] {
6, 10, 10, 13, 11, 13, 16, 16, 16, 16,
18, 18, 18, 18, 18, 23, 23, 23, 23, 23,
23, 25, 25, 25, 25, 25, 25, 25, 27, 27,
27, 27, 27, 27, 27, 27, 29, 29, 29, 29,
29, 29, 29, 31, 31, 31, 31, 31, 31, 33,
33, 33, 33, 33, 36, 36, 36, 36, 38, 38,
38, 40, 40, 42 };
public static final int[] defaultScalingList8x8Inter = new int[] {
9, 13, 13, 15, 13, 15, 17, 17, 17, 17,
19, 19, 19, 19, 19, 21, 21, 21, 21, 21,
21, 22, 22, 22, 22, 22, 22, 22, 24, 24,
24, 24, 24, 24, 24, 24, 25, 25, 25, 25,
25, 25, 25, 27, 27, 27, 27, 27, 27, 28,
28, 28, 28, 28, 30, 30, 30, 30, 32, 32,
32, 33, 33, 35 };
}

View file

@ -0,0 +1,399 @@
package org.jcodec.codecs.h264;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import org.jcodec.codecs.h264.decode.DeblockerInput;
import org.jcodec.codecs.h264.decode.FrameReader;
import org.jcodec.codecs.h264.decode.SliceDecoder;
import org.jcodec.codecs.h264.decode.SliceHeaderReader;
import org.jcodec.codecs.h264.decode.SliceReader;
import org.jcodec.codecs.h264.decode.deblock.DeblockingFilter;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
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.UsedViaReflection;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.VideoDecoder;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rect;
import org.jcodec.common.model.Size;
import org.jcodec.common.tools.MathUtil;
public class H264Decoder extends VideoDecoder {
private Frame[] sRefs;
private IntObjectMap<Frame> lRefs;
private List<Frame> pictureBuffer;
private POCManager poc;
private FrameReader reader;
private ExecutorService tp;
private boolean threaded;
public void shutdownThreadPool() {
if (this.threaded)
this.tp.shutdown();
}
public H264Decoder() {
this.pictureBuffer = new ArrayList<>();
this.poc = new POCManager();
this.threaded = (Runtime.getRuntime().availableProcessors() > 1);
if (this.threaded)
this.tp = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(false);
return t;
}
});
this.reader = new FrameReader();
}
public static H264Decoder createH264DecoderFromCodecPrivate(ByteBuffer codecPrivate) {
H264Decoder d = new H264Decoder();
for (ByteBuffer bb : H264Utils.splitFrame(codecPrivate.duplicate())) {
NALUnit nu = NALUnit.read(bb);
if (nu.type == NALUnitType.SPS) {
d.reader.addSps(bb);
continue;
}
if (nu.type == NALUnitType.PPS)
d.reader.addPps(bb);
}
return d;
}
public Frame decodeFrame(ByteBuffer data, byte[][] buffer) {
return decodeFrameFromNals(H264Utils.splitFrame(data), buffer);
}
public Frame decodeFrameFromNals(List<ByteBuffer> nalUnits, byte[][] buffer) {
return new FrameDecoder(this).decodeFrame(nalUnits, buffer);
}
private static final class SliceDecoderRunnable implements Runnable {
private final SliceReader sliceReader;
private final Frame result;
private H264Decoder.FrameDecoder fdec;
private SliceDecoderRunnable(H264Decoder.FrameDecoder fdec, SliceReader sliceReader, Frame result) {
this.fdec = fdec;
this.sliceReader = sliceReader;
this.result = result;
}
public void run() {
SliceDecoder sliceDecoder = new SliceDecoder(this.fdec.activeSps, this.fdec.dec.sRefs, this.fdec.dec.lRefs, this.fdec.di, this.result);
sliceDecoder.decodeFromReader(this.sliceReader);
sliceDecoder = null;
}
}
static class FrameDecoder {
private SeqParameterSet activeSps;
private DeblockingFilter filter;
private SliceHeader firstSliceHeader;
private NALUnit firstNu;
private H264Decoder dec;
private DeblockerInput di;
public FrameDecoder(H264Decoder decoder) {
this.dec = decoder;
}
public Frame decodeFrame(List<ByteBuffer> nalUnits, byte[][] buffer) {
List<SliceReader> sliceReaders = this.dec.reader.readFrame(nalUnits);
if (sliceReaders == null || sliceReaders.size() == 0)
return null;
Frame result = init(sliceReaders.get(0), buffer);
if (this.dec.threaded && sliceReaders.size() > 1) {
List<Future<?>> futures = new ArrayList<>();
for (SliceReader sliceReader : sliceReaders)
futures.add(this.dec.tp.submit(new H264Decoder.SliceDecoderRunnable(this, sliceReader, result)));
for (Future<?> future : futures)
waitForSure(future);
} else {
for (SliceReader sliceReader : sliceReaders)
new SliceDecoder(this.activeSps, this.dec.sRefs, this.dec.lRefs, this.di, result).decodeFromReader(sliceReader);
}
this.filter.deblockFrame(result);
updateReferences(result);
return result;
}
private void waitForSure(Future<?> future) {
try {
future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void updateReferences(Frame picture) {
if (this.firstNu.nal_ref_idc != 0)
if (this.firstNu.type == NALUnitType.IDR_SLICE) {
performIDRMarking(this.firstSliceHeader.refPicMarkingIDR, picture);
} else {
performMarking(this.firstSliceHeader.refPicMarkingNonIDR, picture);
}
}
private Frame init(SliceReader sliceReader, byte[][] buffer) {
this.firstNu = sliceReader.getNALUnit();
this.firstSliceHeader = sliceReader.getSliceHeader();
this.activeSps = this.firstSliceHeader.sps;
validateSupportedFeatures(this.firstSliceHeader.sps, this.firstSliceHeader.pps);
int picWidthInMbs = this.activeSps.picWidthInMbsMinus1 + 1;
if (this.dec.sRefs == null) {
this.dec.sRefs = new Frame[1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4];
this.dec.lRefs = new IntObjectMap<>();
}
this.di = new DeblockerInput(this.activeSps);
Frame result = H264Decoder.createFrame(this.activeSps, buffer, this.firstSliceHeader.frameNum, this.firstSliceHeader.sliceType, this.di.mvs, this.di.refsUsed,
this.dec.poc.calcPOC(this.firstSliceHeader, this.firstNu));
this.filter = new DeblockingFilter(picWidthInMbs, this.activeSps.bitDepthChromaMinus8 + 8, this.di);
return result;
}
private void validateSupportedFeatures(SeqParameterSet sps, PictureParameterSet pps) {
if (sps.mbAdaptiveFrameFieldFlag)
throw new RuntimeException("Unsupported h264 feature: MBAFF.");
if (sps.bitDepthLumaMinus8 != 0 || sps.bitDepthChromaMinus8 != 0)
throw new RuntimeException("Unsupported h264 feature: High bit depth.");
if (sps.chromaFormatIdc != ColorSpace.YUV420J)
throw new RuntimeException("Unsupported h264 feature: " + String.valueOf(sps.chromaFormatIdc) + " color.");
if (!sps.frameMbsOnlyFlag || sps.fieldPicFlag)
throw new RuntimeException("Unsupported h264 feature: interlace.");
if (pps.constrainedIntraPredFlag)
throw new RuntimeException("Unsupported h264 feature: constrained intra prediction.");
if (sps.qpprimeYZeroTransformBypassFlag)
throw new RuntimeException("Unsupported h264 feature: qprime zero transform bypass.");
if (sps.profileIdc != 66 && sps.profileIdc != 77 && sps.profileIdc != 100)
throw new RuntimeException("Unsupported h264 feature: " + sps.profileIdc + " profile.");
}
public void performIDRMarking(RefPicMarkingIDR refPicMarkingIDR, Frame picture) {
clearAll();
this.dec.pictureBuffer.clear();
Frame saved = saveRef(picture);
if (refPicMarkingIDR.isUseForlongTerm()) {
this.dec.lRefs.put(0, saved);
saved.setShortTerm(false);
} else {
this.dec.sRefs[this.firstSliceHeader.frameNum] = saved;
}
}
private Frame saveRef(Frame decoded) {
Frame frame = (this.dec.pictureBuffer.size() > 0) ? (Frame)this.dec.pictureBuffer.remove(0) : Frame.createFrame(decoded);
frame.copyFromFrame(decoded);
return frame;
}
private void releaseRef(Frame picture) {
if (picture != null)
this.dec.pictureBuffer.add(picture);
}
public void clearAll() {
for (int i = 0; i < this.dec.sRefs.length; i++) {
releaseRef(this.dec.sRefs[i]);
this.dec.sRefs[i] = null;
}
int[] keys = this.dec.lRefs.keys();
for (int j = 0; j < keys.length; j++)
releaseRef(this.dec.lRefs.get(keys[j]));
this.dec.lRefs.clear();
}
public void performMarking(RefPicMarking refPicMarking, Frame picture) {
Frame saved = saveRef(picture);
if (refPicMarking != null) {
RefPicMarking.Instruction[] instructions = refPicMarking.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction instr = instructions[i];
switch (instr.getType()) {
case REMOVE_SHORT:
unrefShortTerm(instr.getArg1());
break;
case REMOVE_LONG:
unrefLongTerm(instr.getArg1());
break;
case CONVERT_INTO_LONG:
convert(instr.getArg1(), instr.getArg2());
break;
case TRUNK_LONG:
truncateLongTerm(instr.getArg1() - 1);
break;
case CLEAR:
clearAll();
break;
case MARK_LONG:
saveLong(saved, instr.getArg1());
saved = null;
break;
}
}
}
if (saved != null)
saveShort(saved);
int maxFrames = 1 << this.activeSps.log2MaxFrameNumMinus4 + 4;
if (refPicMarking == null) {
int maxShort = Math.max(1, this.activeSps.numRefFrames - this.dec.lRefs.size());
int min = Integer.MAX_VALUE, num = 0, minFn = 0;
for (int i = 0; i < this.dec.sRefs.length; i++) {
if (this.dec.sRefs[i] != null) {
int fnWrap = unwrap(this.firstSliceHeader.frameNum, this.dec.sRefs[i].getFrameNo(), maxFrames);
if (fnWrap < min) {
min = fnWrap;
minFn = this.dec.sRefs[i].getFrameNo();
}
num++;
}
}
if (num > maxShort) {
releaseRef(this.dec.sRefs[minFn]);
this.dec.sRefs[minFn] = null;
}
}
}
private int unwrap(int thisFrameNo, int refFrameNo, int maxFrames) {
return (refFrameNo > thisFrameNo) ? (refFrameNo - maxFrames) : refFrameNo;
}
private void saveShort(Frame saved) {
this.dec.sRefs[this.firstSliceHeader.frameNum] = saved;
}
private void saveLong(Frame saved, int longNo) {
Frame prev = this.dec.lRefs.get(longNo);
if (prev != null)
releaseRef(prev);
saved.setShortTerm(false);
this.dec.lRefs.put(longNo, saved);
}
private void truncateLongTerm(int maxLongNo) {
int[] keys = this.dec.lRefs.keys();
for (int i = 0; i < keys.length; i++) {
if (keys[i] > maxLongNo) {
releaseRef(this.dec.lRefs.get(keys[i]));
this.dec.lRefs.remove(keys[i]);
}
}
}
private void convert(int shortNo, int longNo) {
int ind = MathUtil.wrap(this.firstSliceHeader.frameNum - shortNo, 1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4);
releaseRef(this.dec.lRefs.get(longNo));
this.dec.lRefs.put(longNo, this.dec.sRefs[ind]);
this.dec.sRefs[ind] = null;
this.dec.lRefs.get(longNo).setShortTerm(false);
}
private void unrefLongTerm(int longNo) {
releaseRef(this.dec.lRefs.get(longNo));
this.dec.lRefs.remove(longNo);
}
private void unrefShortTerm(int shortNo) {
int ind = MathUtil.wrap(this.firstSliceHeader.frameNum - shortNo, 1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4);
releaseRef(this.dec.sRefs[ind]);
this.dec.sRefs[ind] = null;
}
}
public static Frame createFrame(SeqParameterSet sps, byte[][] buffer, int frameNum, SliceType frameType, H264Utils.MvList2D mvs, Frame[][][] refsUsed, int POC) {
int width = sps.picWidthInMbsMinus1 + 1 << 4;
int height = SeqParameterSet.getPicHeightInMbs(sps) << 4;
Rect crop = null;
if (sps.frameCroppingFlag) {
int sX = sps.frameCropLeftOffset << 1;
int sY = sps.frameCropTopOffset << 1;
int w = width - (sps.frameCropRightOffset << 1) - sX;
int h = height - (sps.frameCropBottomOffset << 1) - sY;
crop = new Rect(sX, sY, w, h);
}
return new Frame(width, height, buffer, ColorSpace.YUV420, crop, frameNum, frameType, mvs, refsUsed, POC);
}
public void addSps(List<ByteBuffer> spsList) {
this.reader.addSpsList(spsList);
}
public void addPps(List<ByteBuffer> ppsList) {
this.reader.addPpsList(ppsList);
}
@UsedViaReflection
public static int probe(ByteBuffer data) {
boolean validSps = false, validPps = false, validSh = false;
for (ByteBuffer nalUnit : H264Utils.splitFrame(data.duplicate())) {
NALUnit marker = NALUnit.read(nalUnit);
if (marker.type == NALUnitType.IDR_SLICE || marker.type == NALUnitType.NON_IDR_SLICE) {
BitReader reader = BitReader.createBitReader(nalUnit);
validSh = validSh(SliceHeaderReader.readPart1(reader));
break;
}
if (marker.type == NALUnitType.SPS) {
validSps = validSps(SeqParameterSet.read(nalUnit));
continue;
}
if (marker.type == NALUnitType.PPS)
validPps = validPps(PictureParameterSet.read(nalUnit));
}
return (validSh ? 60 : 0) + (validSps ? 20 : 0) + (validPps ? 20 : 0);
}
private static boolean validSh(SliceHeader sh) {
return (sh.firstMbInSlice == 0 && sh.sliceType != null && sh.picParameterSetId < 2);
}
private static boolean validSps(SeqParameterSet sps) {
return (sps.bitDepthChromaMinus8 < 4 && sps.bitDepthLumaMinus8 < 4 && sps.chromaFormatIdc != null && sps.seqParameterSetId < 2 && sps.picOrderCntType <= 2);
}
private static boolean validPps(PictureParameterSet pps) {
return (pps.picInitQpMinus26 <= 26 && pps.seqParameterSetId <= 2 && pps.picParameterSetId <= 2);
}
public VideoCodecMeta getCodecMeta(ByteBuffer data) {
List<ByteBuffer> rawSPS = H264Utils.getRawSPS(data.duplicate());
List<ByteBuffer> rawPPS = H264Utils.getRawPPS(data.duplicate());
if (rawSPS.size() == 0) {
Logger.warn("Can not extract metadata from the packet not containing an SPS.");
return null;
}
SeqParameterSet sps = SeqParameterSet.read(rawSPS.get(0));
Size size = H264Utils.getPicSize(sps);
return VideoCodecMeta.createSimpleVideoCodecMeta(size, ColorSpace.YUV420);
}
}

View file

@ -0,0 +1,392 @@
package org.jcodec.codecs.h264;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import org.jcodec.codecs.h264.decode.DeblockerInput;
import org.jcodec.codecs.h264.decode.FrameReader;
import org.jcodec.codecs.h264.decode.SliceDecoder;
import org.jcodec.codecs.h264.decode.SliceHeaderReader;
import org.jcodec.codecs.h264.decode.SliceReader;
import org.jcodec.codecs.h264.decode.deblock.DeblockingFilter;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
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.UsedViaReflection;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.VideoDecoder;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rect;
import org.jcodec.common.model.Size;
import org.jcodec.common.tools.MathUtil;
public class H264Decoder2 extends VideoDecoder {
private Frame[] sRefs;
private IntObjectMap<Frame> lRefs;
private List<Frame> pictureBuffer = new ArrayList<>();
private POCManager poc = new POCManager();
private FrameReader reader;
private ExecutorService tp;
private boolean threaded = (Runtime.getRuntime().availableProcessors() > 1);
public H264Decoder2() {
if (this.threaded)
this.tp = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
}
});
this.reader = new FrameReader();
}
public static H264Decoder2 createH264DecoderFromCodecPrivate(ByteBuffer codecPrivate) {
H264Decoder2 d = new H264Decoder2();
for (ByteBuffer bb : H264Utils.splitFrame(codecPrivate.duplicate())) {
NALUnit nu = NALUnit.read(bb);
if (nu.type == NALUnitType.SPS) {
d.reader.addSps(bb);
continue;
}
if (nu.type == NALUnitType.PPS)
d.reader.addPps(bb);
}
return d;
}
public Frame decodeFrame(ByteBuffer data, byte[][] buffer) {
return decodeFrameFromNals(H264Utils.splitFrame(data), buffer);
}
public Frame decodeFrameFromNals(List<ByteBuffer> nalUnits, byte[][] buffer) {
return new FrameDecoder(this).decodeFrame(nalUnits, buffer);
}
private static final class SliceDecoderRunnable implements Runnable {
private final SliceReader sliceReader;
private final Frame result;
private H264Decoder2.FrameDecoder fdec;
private SliceDecoderRunnable(H264Decoder2.FrameDecoder fdec, SliceReader sliceReader, Frame result) {
this.fdec = fdec;
this.sliceReader = sliceReader;
this.result = result;
}
public void run() {
SliceDecoder sliceDecoder = new SliceDecoder(this.fdec.activeSps, this.fdec.dec.sRefs, this.fdec.dec.lRefs, this.fdec.di, this.result);
sliceDecoder.decodeFromReader(this.sliceReader);
sliceDecoder = null;
Thread.currentThread().interrupt();
}
}
static class FrameDecoder {
private SeqParameterSet activeSps;
private DeblockingFilter filter;
private SliceHeader firstSliceHeader;
private NALUnit firstNu;
private H264Decoder2 dec;
private DeblockerInput di;
public FrameDecoder(H264Decoder2 decoder) {
this.dec = decoder;
}
public Frame decodeFrame(List<ByteBuffer> nalUnits, byte[][] buffer) {
List<SliceReader> sliceReaders = this.dec.reader.readFrame(nalUnits);
if (sliceReaders == null || sliceReaders.size() == 0)
return null;
Frame result = init(sliceReaders.get(0), buffer);
if (this.dec.threaded && sliceReaders.size() > 1) {
List<Future<?>> futures = new ArrayList<>();
for (SliceReader sliceReader : sliceReaders)
futures.add(this.dec.tp.submit(new H264Decoder2.SliceDecoderRunnable(this, sliceReader, result)));
for (Future<?> future : futures)
waitForSure(future);
} else {
for (SliceReader sliceReader : sliceReaders)
new SliceDecoder(this.activeSps, this.dec.sRefs, this.dec.lRefs, this.di, result).decodeFromReader(sliceReader);
}
this.filter.deblockFrame(result);
updateReferences(result);
return result;
}
private void waitForSure(Future<?> future) {
try {
future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void updateReferences(Frame picture) {
if (this.firstNu.nal_ref_idc != 0)
if (this.firstNu.type == NALUnitType.IDR_SLICE) {
performIDRMarking(this.firstSliceHeader.refPicMarkingIDR, picture);
} else {
performMarking(this.firstSliceHeader.refPicMarkingNonIDR, picture);
}
}
private Frame init(SliceReader sliceReader, byte[][] buffer) {
this.firstNu = sliceReader.getNALUnit();
this.firstSliceHeader = sliceReader.getSliceHeader();
this.activeSps = this.firstSliceHeader.sps;
validateSupportedFeatures(this.firstSliceHeader.sps, this.firstSliceHeader.pps);
int picWidthInMbs = this.activeSps.picWidthInMbsMinus1 + 1;
if (this.dec.sRefs == null) {
this.dec.sRefs = new Frame[1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4];
this.dec.lRefs = new IntObjectMap<>();
}
this.di = new DeblockerInput(this.activeSps);
Frame result = H264Decoder2.createFrame(this.activeSps, buffer, this.firstSliceHeader.frameNum, this.firstSliceHeader.sliceType, this.di.mvs, this.di.refsUsed,
this.dec.poc.calcPOC(this.firstSliceHeader, this.firstNu));
this.filter = new DeblockingFilter(picWidthInMbs, this.activeSps.bitDepthChromaMinus8 + 8, this.di);
return result;
}
private void validateSupportedFeatures(SeqParameterSet sps, PictureParameterSet pps) {
if (sps.mbAdaptiveFrameFieldFlag)
throw new RuntimeException("Unsupported h264 feature: MBAFF.");
if (sps.bitDepthLumaMinus8 != 0 || sps.bitDepthChromaMinus8 != 0)
throw new RuntimeException("Unsupported h264 feature: High bit depth.");
if (sps.chromaFormatIdc != ColorSpace.YUV420J)
throw new RuntimeException("Unsupported h264 feature: " + String.valueOf(sps.chromaFormatIdc) + " color.");
if (!sps.frameMbsOnlyFlag || sps.fieldPicFlag)
throw new RuntimeException("Unsupported h264 feature: interlace.");
if (pps.constrainedIntraPredFlag)
throw new RuntimeException("Unsupported h264 feature: constrained intra prediction.");
if (sps.qpprimeYZeroTransformBypassFlag)
throw new RuntimeException("Unsupported h264 feature: qprime zero transform bypass.");
if (sps.profileIdc != 66 && sps.profileIdc != 77 && sps.profileIdc != 100)
throw new RuntimeException("Unsupported h264 feature: " + sps.profileIdc + " profile.");
}
public void performIDRMarking(RefPicMarkingIDR refPicMarkingIDR, Frame picture) {
clearAll();
this.dec.pictureBuffer.clear();
Frame saved = saveRef(picture);
if (refPicMarkingIDR.isUseForlongTerm()) {
this.dec.lRefs.put(0, saved);
saved.setShortTerm(false);
} else {
this.dec.sRefs[this.firstSliceHeader.frameNum] = saved;
}
}
private Frame saveRef(Frame decoded) {
Frame frame = (this.dec.pictureBuffer.size() > 0) ? (Frame)this.dec.pictureBuffer.remove(0) : Frame.createFrame(decoded);
frame.copyFromFrame(decoded);
return frame;
}
private void releaseRef(Frame picture) {
if (picture != null)
this.dec.pictureBuffer.add(picture);
}
public void clearAll() {
for (int i = 0; i < this.dec.sRefs.length; i++) {
releaseRef(this.dec.sRefs[i]);
this.dec.sRefs[i] = null;
}
int[] keys = this.dec.lRefs.keys();
for (int j = 0; j < keys.length; j++)
releaseRef(this.dec.lRefs.get(keys[j]));
this.dec.lRefs.clear();
}
public void performMarking(RefPicMarking refPicMarking, Frame picture) {
Frame saved = saveRef(picture);
if (refPicMarking != null) {
RefPicMarking.Instruction[] instructions = refPicMarking.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction instr = instructions[i];
switch (instr.getType()) {
case REMOVE_SHORT:
unrefShortTerm(instr.getArg1());
break;
case REMOVE_LONG:
unrefLongTerm(instr.getArg1());
break;
case CONVERT_INTO_LONG:
convert(instr.getArg1(), instr.getArg2());
break;
case TRUNK_LONG:
truncateLongTerm(instr.getArg1() - 1);
break;
case CLEAR:
clearAll();
break;
case MARK_LONG:
saveLong(saved, instr.getArg1());
saved = null;
break;
}
}
}
if (saved != null)
saveShort(saved);
int maxFrames = 1 << this.activeSps.log2MaxFrameNumMinus4 + 4;
if (refPicMarking == null) {
int maxShort = Math.max(1, this.activeSps.numRefFrames - this.dec.lRefs.size());
int min = Integer.MAX_VALUE, num = 0, minFn = 0;
for (int i = 0; i < this.dec.sRefs.length; i++) {
if (this.dec.sRefs[i] != null) {
int fnWrap = unwrap(this.firstSliceHeader.frameNum, this.dec.sRefs[i].getFrameNo(), maxFrames);
if (fnWrap < min) {
min = fnWrap;
minFn = this.dec.sRefs[i].getFrameNo();
}
num++;
}
}
if (num > maxShort) {
releaseRef(this.dec.sRefs[minFn]);
this.dec.sRefs[minFn] = null;
}
}
}
private int unwrap(int thisFrameNo, int refFrameNo, int maxFrames) {
return (refFrameNo > thisFrameNo) ? (refFrameNo - maxFrames) : refFrameNo;
}
private void saveShort(Frame saved) {
this.dec.sRefs[this.firstSliceHeader.frameNum] = saved;
}
private void saveLong(Frame saved, int longNo) {
Frame prev = this.dec.lRefs.get(longNo);
if (prev != null)
releaseRef(prev);
saved.setShortTerm(false);
this.dec.lRefs.put(longNo, saved);
}
private void truncateLongTerm(int maxLongNo) {
int[] keys = this.dec.lRefs.keys();
for (int i = 0; i < keys.length; i++) {
if (keys[i] > maxLongNo) {
releaseRef(this.dec.lRefs.get(keys[i]));
this.dec.lRefs.remove(keys[i]);
}
}
}
private void convert(int shortNo, int longNo) {
int ind = MathUtil.wrap(this.firstSliceHeader.frameNum - shortNo, 1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4);
releaseRef(this.dec.lRefs.get(longNo));
this.dec.lRefs.put(longNo, this.dec.sRefs[ind]);
this.dec.sRefs[ind] = null;
this.dec.lRefs.get(longNo).setShortTerm(false);
}
private void unrefLongTerm(int longNo) {
releaseRef(this.dec.lRefs.get(longNo));
this.dec.lRefs.remove(longNo);
}
private void unrefShortTerm(int shortNo) {
int ind = MathUtil.wrap(this.firstSliceHeader.frameNum - shortNo, 1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4);
releaseRef(this.dec.sRefs[ind]);
this.dec.sRefs[ind] = null;
}
}
public static Frame createFrame(SeqParameterSet sps, byte[][] buffer, int frameNum, SliceType frameType, H264Utils.MvList2D mvs, Frame[][][] refsUsed, int POC) {
int width = sps.picWidthInMbsMinus1 + 1 << 4;
int height = SeqParameterSet.getPicHeightInMbs(sps) << 4;
Rect crop = null;
if (sps.frameCroppingFlag) {
int sX = sps.frameCropLeftOffset << 1;
int sY = sps.frameCropTopOffset << 1;
int w = width - (sps.frameCropRightOffset << 1) - sX;
int h = height - (sps.frameCropBottomOffset << 1) - sY;
crop = new Rect(sX, sY, w, h);
}
return new Frame(width, height, buffer, ColorSpace.YUV420, crop, frameNum, frameType, mvs, refsUsed, POC);
}
public void addSps(List<ByteBuffer> spsList) {
this.reader.addSpsList(spsList);
}
public void addPps(List<ByteBuffer> ppsList) {
this.reader.addPpsList(ppsList);
}
@UsedViaReflection
public static int probe(ByteBuffer data) {
boolean validSps = false, validPps = false, validSh = false;
for (ByteBuffer nalUnit : H264Utils.splitFrame(data.duplicate())) {
NALUnit marker = NALUnit.read(nalUnit);
if (marker.type == NALUnitType.IDR_SLICE || marker.type == NALUnitType.NON_IDR_SLICE) {
BitReader reader = BitReader.createBitReader(nalUnit);
validSh = validSh(SliceHeaderReader.readPart1(reader));
break;
}
if (marker.type == NALUnitType.SPS) {
validSps = validSps(SeqParameterSet.read(nalUnit));
continue;
}
if (marker.type == NALUnitType.PPS)
validPps = validPps(PictureParameterSet.read(nalUnit));
}
return (validSh ? 60 : 0) + (validSps ? 20 : 0) + (validPps ? 20 : 0);
}
private static boolean validSh(SliceHeader sh) {
return (sh.firstMbInSlice == 0 && sh.sliceType != null && sh.picParameterSetId < 2);
}
private static boolean validSps(SeqParameterSet sps) {
return (sps.bitDepthChromaMinus8 < 4 && sps.bitDepthLumaMinus8 < 4 && sps.chromaFormatIdc != null && sps.seqParameterSetId < 2 && sps.picOrderCntType <= 2);
}
private static boolean validPps(PictureParameterSet pps) {
return (pps.picInitQpMinus26 <= 26 && pps.seqParameterSetId <= 2 && pps.picParameterSetId <= 2);
}
public VideoCodecMeta getCodecMeta(ByteBuffer data) {
List<ByteBuffer> rawSPS = H264Utils.getRawSPS(data.duplicate());
List<ByteBuffer> rawPPS = H264Utils.getRawPPS(data.duplicate());
if (rawSPS.size() == 0) {
Logger.warn("Can not extract metadata from the packet not containing an SPS.");
return null;
}
SeqParameterSet sps = SeqParameterSet.read(rawSPS.get(0));
Size size = H264Utils.getPicSize(sps);
return VideoCodecMeta.createSimpleVideoCodecMeta(size, ColorSpace.YUV420);
}
}

View file

@ -0,0 +1,322 @@
package org.jcodec.codecs.h264;
import java.nio.ByteBuffer;
import org.jcodec.codecs.h264.encode.DumbRateControl;
import org.jcodec.codecs.h264.encode.EncodedMB;
import org.jcodec.codecs.h264.encode.MBEncoderHelper;
import org.jcodec.codecs.h264.encode.MBEncoderI16x16;
import org.jcodec.codecs.h264.encode.MBEncoderP16x16;
import org.jcodec.codecs.h264.encode.MotionEstimator;
import org.jcodec.codecs.h264.encode.RateControl;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
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.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.codecs.h264.io.write.SliceHeaderWriter;
import org.jcodec.common.VideoEncoder;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Size;
import org.jcodec.common.tools.MathUtil;
public class H264Encoder extends VideoEncoder {
private static final int KEY_INTERVAL_DEFAULT = 25;
private static final int MOTION_SEARCH_RANGE_DEFAULT = 16;
private CAVLC[] cavlc;
private byte[][] leftRow;
private byte[][] topLine;
private RateControl rc;
private int frameNumber;
private int keyInterval;
private int motionSearchRange;
private int maxPOC;
private int maxFrameNumber;
private SeqParameterSet sps;
private PictureParameterSet pps;
private MBEncoderI16x16 mbEncoderI16x16;
private MBEncoderP16x16 mbEncoderP16x16;
private Picture ref;
private Picture picOut;
private EncodedMB[] topEncoded;
private EncodedMB outMB;
public static H264Encoder createH264Encoder() {
return new H264Encoder(new DumbRateControl());
}
public H264Encoder(RateControl rc) {
this.rc = rc;
this.keyInterval = 25;
this.motionSearchRange = 16;
}
public int getKeyInterval() {
return this.keyInterval;
}
public void setKeyInterval(int keyInterval) {
this.keyInterval = keyInterval;
}
public int getMotionSearchRange() {
return this.motionSearchRange;
}
public void setMotionSearchRange(int motionSearchRange) {
this.motionSearchRange = motionSearchRange;
}
public VideoEncoder.EncodedFrame encodeFrame(Picture pic, ByteBuffer _out) {
if (pic.getColor() != ColorSpace.YUV420J)
throw new IllegalArgumentException("Input picture color is not supported: " + String.valueOf(pic.getColor()));
if (this.frameNumber >= this.keyInterval)
this.frameNumber = 0;
SliceType sliceType = (this.frameNumber == 0) ? SliceType.I : SliceType.P;
boolean idr = (this.frameNumber == 0);
ByteBuffer data = doEncodeFrame(pic, _out, idr, this.frameNumber++, sliceType);
return new VideoEncoder.EncodedFrame(data, idr);
}
public ByteBuffer encodeIDRFrame(Picture pic, ByteBuffer _out) {
this.frameNumber = 0;
return doEncodeFrame(pic, _out, true, this.frameNumber, SliceType.I);
}
public ByteBuffer encodePFrame(Picture pic, ByteBuffer _out) {
this.frameNumber++;
return doEncodeFrame(pic, _out, true, this.frameNumber, SliceType.P);
}
public ByteBuffer doEncodeFrame(Picture pic, ByteBuffer _out, boolean idr, int frameNumber, SliceType frameType) {
ByteBuffer dup = _out.duplicate();
int maxSize = Math.min(dup.remaining(), pic.getWidth() * pic.getHeight());
maxSize -= maxSize >>> 6;
int qp = this.rc.startPicture(pic.getSize(), maxSize, frameType);
if (idr) {
this.sps = initSPS(new Size(pic.getCroppedWidth(), pic.getCroppedHeight()));
this.pps = initPPS();
this.maxPOC = 1 << this.sps.log2MaxPicOrderCntLsbMinus4 + 4;
this.maxFrameNumber = 1 << this.sps.log2MaxFrameNumMinus4 + 4;
}
if (idr) {
dup.putInt(1);
new NALUnit(NALUnitType.SPS, 3).write(dup);
writeSPS(dup, this.sps);
dup.putInt(1);
new NALUnit(NALUnitType.PPS, 3).write(dup);
writePPS(dup, this.pps);
}
int mbWidth = this.sps.picWidthInMbsMinus1 + 1;
int mbHeight = this.sps.picHeightInMapUnitsMinus1 + 1;
this.leftRow = new byte[][] { new byte[16], new byte[8], new byte[8] };
this.topLine = new byte[][] { new byte[mbWidth << 4], new byte[mbWidth << 3], new byte[mbWidth << 3] };
this.picOut = Picture.create(mbWidth << 4, mbHeight << 4, ColorSpace.YUV420J);
this.outMB = new EncodedMB();
this.topEncoded = new EncodedMB[mbWidth];
for (int i = 0; i < mbWidth; i++)
this.topEncoded[i] = new EncodedMB();
encodeSlice(this.sps, this.pps, pic, dup, idr, frameNumber, frameType, qp);
putLastMBLine();
this.ref = this.picOut;
dup.flip();
return dup;
}
private void writePPS(ByteBuffer dup, PictureParameterSet pps) {
ByteBuffer tmp = ByteBuffer.allocate(1024);
pps.write(tmp);
tmp.flip();
H264Utils.escapeNAL(tmp, dup);
}
private void writeSPS(ByteBuffer dup, SeqParameterSet sps) {
ByteBuffer tmp = ByteBuffer.allocate(1024);
sps.write(tmp);
tmp.flip();
H264Utils.escapeNAL(tmp, dup);
}
public PictureParameterSet initPPS() {
PictureParameterSet pps = new PictureParameterSet();
pps.picInitQpMinus26 = 0;
return pps;
}
public SeqParameterSet initSPS(Size sz) {
SeqParameterSet sps = new SeqParameterSet();
sps.picWidthInMbsMinus1 = (sz.getWidth() + 15 >> 4) - 1;
sps.picHeightInMapUnitsMinus1 = (sz.getHeight() + 15 >> 4) - 1;
sps.chromaFormatIdc = ColorSpace.YUV420J;
sps.profileIdc = 66;
sps.levelIdc = 40;
sps.numRefFrames = 1;
sps.frameMbsOnlyFlag = true;
sps.log2MaxFrameNumMinus4 = Math.max(0, MathUtil.log2(this.keyInterval) - 3);
int codedWidth = sps.picWidthInMbsMinus1 + 1 << 4;
int codedHeight = sps.picHeightInMapUnitsMinus1 + 1 << 4;
sps.frameCroppingFlag = (codedWidth != sz.getWidth() || codedHeight != sz.getHeight());
sps.frameCropRightOffset = codedWidth - sz.getWidth() + 1 >> 1;
sps.frameCropBottomOffset = codedHeight - sz.getHeight() + 1 >> 1;
return sps;
}
private void encodeSlice(SeqParameterSet sps, PictureParameterSet pps, Picture pic, ByteBuffer dup, boolean idr, int frameNum, SliceType sliceType, int qp) {
if (idr && sliceType != SliceType.I) {
idr = false;
Logger.warn("Illegal value of idr = true when sliceType != I");
}
this.cavlc = new CAVLC[] { new CAVLC(sps, pps, 2, 2), new CAVLC(sps, pps, 1, 1), new CAVLC(sps, pps, 1, 1) };
this.mbEncoderI16x16 = new MBEncoderI16x16(this.cavlc, this.leftRow, this.topLine);
this.mbEncoderP16x16 = new MBEncoderP16x16(sps, this.ref, this.cavlc, new MotionEstimator(this.motionSearchRange));
dup.putInt(1);
new NALUnit(idr ? NALUnitType.IDR_SLICE : NALUnitType.NON_IDR_SLICE, 3).write(dup);
SliceHeader sh = new SliceHeader();
sh.sliceType = sliceType;
if (idr)
sh.refPicMarkingIDR = new RefPicMarkingIDR(false, false);
sh.pps = pps;
sh.sps = sps;
sh.picOrderCntLsb = (frameNum << 1) % this.maxPOC;
sh.frameNum = frameNum % this.maxFrameNumber;
sh.sliceQpDelta = qp - (pps.picInitQpMinus26 + 26);
ByteBuffer buf = ByteBuffer.allocate(pic.getWidth() * pic.getHeight());
BitWriter sliceData = new BitWriter(buf);
SliceHeaderWriter.write(sh, idr, 2, sliceData);
for (int mbY = 0, mbAddr = 0; mbY < sps.picHeightInMapUnitsMinus1 + 1; mbY++) {
for (int mbX = 0; mbX < sps.picWidthInMbsMinus1 + 1; mbX++, mbAddr++) {
if (sliceType == SliceType.P)
CAVLCWriter.writeUE(sliceData, 0);
MBType mbType = selectMBType(sliceType);
if (mbType == MBType.I_16x16) {
int predMode = this.mbEncoderI16x16.getPredMode(pic, mbX, mbY);
int cbpChroma = this.mbEncoderI16x16.getCbpChroma(pic, mbX, mbY);
int cbpLuma = this.mbEncoderI16x16.getCbpLuma(pic, mbX, mbY);
int i16x16TypeOffset = cbpLuma / 15 * 12 + cbpChroma * 4 + predMode;
int mbTypeOffset = (sliceType == SliceType.P) ? 5 : 0;
CAVLCWriter.writeUE(sliceData, mbTypeOffset + mbType.code() + i16x16TypeOffset);
} else {
CAVLCWriter.writeUE(sliceData, mbType.code());
}
int totalQpDelta = 0;
int qpDelta = this.rc.initialQpDelta();
while (true) {
BitWriter candidate = sliceData.fork();
totalQpDelta += qpDelta;
encodeMacroblock(mbType, pic, mbX, mbY, candidate, qp, totalQpDelta);
qpDelta = this.rc.accept(candidate.position() - sliceData.position());
if (qpDelta != 0)
restoreMacroblock(mbType);
if (qpDelta == 0) {
sliceData = candidate;
qp += totalQpDelta;
collectPredictors(this.outMB.getPixels(), mbX);
addToReference(mbX, mbY);
break;
}
}
}
}
sliceData.write1Bit(1);
sliceData.flush();
buf = sliceData.getBuffer();
buf.flip();
H264Utils.escapeNAL(buf, dup);
}
private void encodeMacroblock(MBType mbType, Picture pic, int mbX, int mbY, BitWriter candidate, int qp, int qpDelta) {
if (mbType == MBType.I_16x16) {
this.mbEncoderI16x16.save();
this.mbEncoderI16x16.encodeMacroblock(pic, mbX, mbY, candidate, this.outMB, (mbX > 0) ? this.topEncoded[mbX - 1] : null,
(mbY > 0) ? this.topEncoded[mbX] : null, qp + qpDelta, qpDelta);
} else if (mbType == MBType.P_16x16) {
this.mbEncoderP16x16.save();
this.mbEncoderP16x16.encodeMacroblock(pic, mbX, mbY, candidate, this.outMB, (mbX > 0) ? this.topEncoded[mbX - 1] : null,
(mbY > 0) ? this.topEncoded[mbX] : null, qp + qpDelta, qpDelta);
} else {
throw new RuntimeException("Macroblock of type " + String.valueOf(mbType) + " is not supported.");
}
}
private void restoreMacroblock(MBType mbType) {
if (mbType == MBType.I_16x16) {
this.mbEncoderI16x16.restore();
} else if (mbType == MBType.P_16x16) {
this.mbEncoderP16x16.restore();
} else {
throw new RuntimeException("Macroblock of type " + String.valueOf(mbType) + " is not supported.");
}
}
private MBType selectMBType(SliceType sliceType) {
if (sliceType == SliceType.I)
return MBType.I_16x16;
if (sliceType == SliceType.P)
return MBType.P_16x16;
throw new RuntimeException("Unsupported slice type");
}
private void addToReference(int mbX, int mbY) {
if (mbY > 0)
MBEncoderHelper.putBlkPic(this.picOut, this.topEncoded[mbX].getPixels(), mbX << 4, mbY - 1 << 4);
EncodedMB tmp = this.topEncoded[mbX];
this.topEncoded[mbX] = this.outMB;
this.outMB = tmp;
}
private void putLastMBLine() {
int mbWidth = this.sps.picWidthInMbsMinus1 + 1;
int mbHeight = this.sps.picHeightInMapUnitsMinus1 + 1;
for (int mbX = 0; mbX < mbWidth; mbX++)
MBEncoderHelper.putBlkPic(this.picOut, this.topEncoded[mbX].getPixels(), mbX << 4, mbHeight - 1 << 4);
}
private void collectPredictors(Picture outMB, int mbX) {
System.arraycopy(outMB.getPlaneData(0), 240, this.topLine[0], mbX << 4, 16);
System.arraycopy(outMB.getPlaneData(1), 56, this.topLine[1], mbX << 3, 8);
System.arraycopy(outMB.getPlaneData(2), 56, this.topLine[2], mbX << 3, 8);
copyCol(outMB.getPlaneData(0), 15, 16, this.leftRow[0]);
copyCol(outMB.getPlaneData(1), 7, 8, this.leftRow[1]);
copyCol(outMB.getPlaneData(2), 7, 8, this.leftRow[2]);
}
private void copyCol(byte[] planeData, int off, int stride, byte[] out) {
for (int i = 0; i < out.length; i++) {
out[i] = planeData[off];
off += stride;
}
}
public ColorSpace[] getSupportedColorSpaces() {
return new ColorSpace[] { ColorSpace.YUV420J };
}
public int estimateBufferSize(Picture frame) {
return Math.max(65536, frame.getWidth() * frame.getHeight() / 2);
}
}

View file

@ -0,0 +1,893 @@
package org.jcodec.codecs.h264;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.jcodec.codecs.h264.decode.SliceHeaderReader;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
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.codecs.h264.io.write.SliceHeaderWriter;
import org.jcodec.codecs.h264.mp4.AvcCBox;
import org.jcodec.common.IntArrayList;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.io.FileChannelWrapper;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Size;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.NodeBox;
import org.jcodec.containers.mp4.boxes.SampleEntry;
import org.jcodec.containers.mp4.boxes.VideoSampleEntry;
public class H264Utils {
public static ByteBuffer nextNALUnit(ByteBuffer buf) {
skipToNALUnit(buf);
if (buf.hasArray())
return gotoNALUnitWithArray(buf);
return gotoNALUnit(buf);
}
public static final void skipToNALUnit(ByteBuffer buf) {
if (!buf.hasRemaining())
return;
int val = -1;
while (buf.hasRemaining()) {
val <<= 8;
val |= buf.get() & 0xFF;
if ((val & 0xFFFFFF) == 1) {
buf.position(buf.position());
break;
}
}
}
public static final ByteBuffer gotoNALUnit(ByteBuffer buf) {
if (!buf.hasRemaining())
return null;
int from = buf.position();
ByteBuffer result = buf.slice();
result.order(ByteOrder.BIG_ENDIAN);
int val = -1;
while (buf.hasRemaining()) {
val <<= 8;
val |= buf.get() & 0xFF;
if ((val & 0xFFFFFF) == 1) {
buf.position(buf.position() - ((val == 1) ? 4 : 3));
result.limit(buf.position() - from);
break;
}
}
return result;
}
public static final ByteBuffer gotoNALUnitWithArray(ByteBuffer buf) {
if (!buf.hasRemaining())
return null;
int from = buf.position();
ByteBuffer result = buf.slice();
result.order(ByteOrder.BIG_ENDIAN);
byte[] arr = buf.array();
int pos = from + buf.arrayOffset();
int posFrom = pos;
int lim = buf.limit() + buf.arrayOffset();
while (pos < lim) {
byte b = arr[pos];
if ((b & 0xFE) == 0) {
while (b == 0) {
if (++pos < lim) {
b = arr[pos];
continue;
}
break;
}
if (b == 1 &&
pos - posFrom >= 2 && arr[pos - 1] == 0 && arr[pos - 2] == 0) {
int lenSize = (pos - posFrom >= 3 && arr[pos - 3] == 0) ? 4 : 3;
buf.position(pos + 1 - buf.arrayOffset() - lenSize);
result.limit(buf.position() - from);
return result;
}
}
pos += 3;
}
buf.position(buf.limit());
return result;
}
public static final void unescapeNAL(ByteBuffer _buf) {
if (_buf.remaining() < 2)
return;
ByteBuffer _in = _buf.duplicate();
ByteBuffer out = _buf.duplicate();
byte p1 = _in.get();
out.put(p1);
byte p2 = _in.get();
out.put(p2);
while (_in.hasRemaining()) {
byte b = _in.get();
if (p1 != 0 || p2 != 0 || b != 3)
out.put(b);
p1 = p2;
p2 = b;
}
_buf.limit(out.position());
}
public static final void escapeNALinplace(ByteBuffer src) {
int[] loc = searchEscapeLocations(src);
int old = src.limit();
src.limit(src.limit() + loc.length);
int newPos = src.limit() - 1, oldPos = old - 1, locIdx = loc.length - 1;
for (; newPos >=
src.position(); newPos--, oldPos--) {
src.put(newPos, src.get(oldPos));
if (locIdx >= 0 && loc[locIdx] == oldPos) {
newPos--;
src.put(newPos, (byte)3);
locIdx--;
}
}
}
private static int[] searchEscapeLocations(ByteBuffer src) {
IntArrayList points = IntArrayList.createIntArrayList();
ByteBuffer search = src.duplicate();
int p = search.getShort();
while (search.hasRemaining()) {
byte b = search.get();
if (p == 0 && (b & 0xFFFFFFFC) == 0) {
points.add(search.position() - 1);
p = 3;
}
p = p << 8 & 0xFFFF;
p |= b & 0xFF;
}
int[] array = points.toArray();
return array;
}
public static final void escapeNAL(ByteBuffer src, ByteBuffer dst) {
byte p1 = src.get(), p2 = src.get();
dst.put(p1);
dst.put(p2);
while (src.hasRemaining()) {
byte b = src.get();
if (p1 == 0 && p2 == 0 && (b & 0xFF) <= 3) {
dst.put((byte)3);
p1 = p2;
p2 = 3;
}
dst.put(b);
p1 = p2;
p2 = b;
}
}
public static List<ByteBuffer> splitMOVPacket(ByteBuffer buf, AvcCBox avcC) {
List<ByteBuffer> result = new ArrayList<>();
int nls = avcC.getNalLengthSize();
ByteBuffer dup = buf.duplicate();
while (dup.remaining() >= nls) {
int len = readLen(dup, nls);
if (len == 0)
break;
result.add(NIOUtils.read(dup, len));
}
return result;
}
private static int readLen(ByteBuffer dup, int nls) {
switch (nls) {
case 1:
return dup.get() & 0xFF;
case 2:
return dup.getShort() & 0xFFFF;
case 3:
return (dup.getShort() & 0xFFFF) << 8 | dup.get() & 0xFF;
case 4:
return dup.getInt();
}
throw new IllegalArgumentException("NAL Unit length size can not be " + nls);
}
public static void encodeMOVPacketInplace(ByteBuffer avcFrame) {
ByteBuffer dup = avcFrame.duplicate();
ByteBuffer d1 = avcFrame.duplicate();
int tot = d1.position();
while (true) {
ByteBuffer buf = nextNALUnit(dup);
if (buf == null)
break;
d1.position(tot);
d1.putInt(buf.remaining());
tot += buf.remaining() + 4;
}
}
public static ByteBuffer encodeMOVPacket(ByteBuffer avcFrame) {
ByteBuffer dup = avcFrame.duplicate();
List<ByteBuffer> list = new ArrayList<>();
int totalLen = 0;
ByteBuffer buf;
while ((buf = nextNALUnit(dup)) != null) {
list.add(buf);
totalLen += buf.remaining();
}
ByteBuffer result = ByteBuffer.allocate(list.size() * 4 + totalLen);
for (ByteBuffer byteBuffer : list) {
result.putInt(byteBuffer.remaining());
result.put(byteBuffer);
}
result.flip();
return result;
}
public static ByteBuffer decodeMOVPacket(ByteBuffer result, AvcCBox avcC) {
if (avcC.getNalLengthSize() == 4) {
decodeMOVPacketInplace(result, avcC);
return result;
}
return joinNALUnits(splitMOVPacket(result, avcC));
}
public static void decodeMOVPacketInplace(ByteBuffer result, AvcCBox avcC) {
if (avcC.getNalLengthSize() != 4)
throw new IllegalArgumentException("Can only inplace decode AVC MOV packet with nal_length_size = 4.");
ByteBuffer dup = result.duplicate();
while (dup.remaining() >= 4) {
int size = dup.getInt();
dup.position(dup.position() - 4);
dup.putInt(1);
dup.position(dup.position() + size);
}
}
public static void wipePS(ByteBuffer _in, ByteBuffer out, List<ByteBuffer> spsList, List<ByteBuffer> ppsList) {
ByteBuffer dup = _in.duplicate();
while (dup.hasRemaining()) {
ByteBuffer buf = nextNALUnit(dup);
if (buf == null)
break;
NALUnit nu = NALUnit.read(buf.duplicate());
if (nu.type == NALUnitType.PPS) {
if (ppsList != null)
ppsList.add(NIOUtils.duplicate(buf));
continue;
}
if (nu.type == NALUnitType.SPS) {
if (spsList != null)
spsList.add(NIOUtils.duplicate(buf));
continue;
}
if (out != null) {
out.putInt(1);
out.put(buf);
}
}
if (out != null)
out.flip();
}
public static void wipePSinplace(ByteBuffer _in, Collection<ByteBuffer> spsList, Collection<ByteBuffer> ppsList) {
ByteBuffer dup = _in.duplicate();
while (dup.hasRemaining()) {
ByteBuffer buf = nextNALUnit(dup);
if (buf == null)
break;
NALUnit nu = NALUnit.read(buf);
if (nu.type == NALUnitType.PPS) {
if (ppsList != null)
ppsList.add(NIOUtils.duplicate(buf));
_in.position(dup.position());
continue;
}
if (nu.type == NALUnitType.SPS) {
if (spsList != null)
spsList.add(NIOUtils.duplicate(buf));
_in.position(dup.position());
continue;
}
if (nu.type == NALUnitType.IDR_SLICE || nu.type == NALUnitType.NON_IDR_SLICE)
break;
}
}
public static AvcCBox createAvcC(SeqParameterSet sps, PictureParameterSet pps, int nalLengthSize) {
ByteBuffer serialSps = ByteBuffer.allocate(512);
sps.write(serialSps);
serialSps.flip();
escapeNALinplace(serialSps);
ByteBuffer serialPps = ByteBuffer.allocate(512);
pps.write(serialPps);
serialPps.flip();
escapeNALinplace(serialPps);
AvcCBox avcC = AvcCBox.createAvcCBox(sps.profileIdc, 0, sps.levelIdc, nalLengthSize, Arrays.asList(serialSps),
Arrays.asList(serialPps));
return avcC;
}
public static AvcCBox createAvcCFromList(List<SeqParameterSet> initSPS, List<PictureParameterSet> initPPS, int nalLengthSize) {
List<ByteBuffer> serialSps = saveSPS(initSPS);
List<ByteBuffer> serialPps = savePPS(initPPS);
SeqParameterSet sps = initSPS.get(0);
return AvcCBox.createAvcCBox(sps.profileIdc, 0, sps.levelIdc, nalLengthSize, serialSps, serialPps);
}
public static List<ByteBuffer> savePPS(List<PictureParameterSet> initPPS) {
List<ByteBuffer> serialPps = new ArrayList<>();
for (PictureParameterSet pps : initPPS) {
ByteBuffer bb1 = ByteBuffer.allocate(512);
pps.write(bb1);
bb1.flip();
escapeNALinplace(bb1);
serialPps.add(bb1);
}
return serialPps;
}
public static List<ByteBuffer> saveSPS(List<SeqParameterSet> initSPS) {
List<ByteBuffer> serialSps = new ArrayList<>();
for (SeqParameterSet sps : initSPS) {
ByteBuffer bb1 = ByteBuffer.allocate(512);
sps.write(bb1);
bb1.flip();
escapeNALinplace(bb1);
serialSps.add(bb1);
}
return serialSps;
}
public static SampleEntry createMOVSampleEntryFromBytes(ByteBuffer codecPrivate) {
List<ByteBuffer> rawSPS = getRawSPS(codecPrivate.duplicate());
List<ByteBuffer> rawPPS = getRawPPS(codecPrivate.duplicate());
return createMOVSampleEntryFromSpsPpsList(rawSPS, rawPPS, 4);
}
public static SampleEntry createMOVSampleEntryFromSpsPpsList(List<ByteBuffer> spsList, List<ByteBuffer> ppsList, int nalLengthSize) {
AvcCBox avcC = createAvcCFromPS(spsList, ppsList, nalLengthSize);
return createMOVSampleEntryFromAvcC(avcC);
}
public static AvcCBox createAvcCFromBytes(ByteBuffer codecPrivate) {
List<ByteBuffer> rawSPS = getRawSPS(codecPrivate.duplicate());
List<ByteBuffer> rawPPS = getRawPPS(codecPrivate.duplicate());
return createAvcCFromPS(rawSPS, rawPPS, 4);
}
public static AvcCBox createAvcCFromPS(List<ByteBuffer> spsList, List<ByteBuffer> ppsList, int nalLengthSize) {
SeqParameterSet sps = readSPS(NIOUtils.duplicate(spsList.get(0)));
return AvcCBox.createAvcCBox(sps.profileIdc, 0, sps.levelIdc, nalLengthSize, spsList, ppsList);
}
public static SampleEntry createMOVSampleEntryFromAvcC(AvcCBox avcC) {
SeqParameterSet sps = SeqParameterSet.read(avcC.getSpsList().get(0).duplicate());
int codedWidth = sps.picWidthInMbsMinus1 + 1 << 4;
int codedHeight = SeqParameterSet.getPicHeightInMbs(sps) << 4;
SampleEntry se = VideoSampleEntry.videoSampleEntry("avc1", getPicSize(sps), "JCodec");
se.add(avcC);
return se;
}
public static SampleEntry createMOVSampleEntryFromSpsPps(SeqParameterSet initSPS, PictureParameterSet initPPS, int nalLengthSize) {
ByteBuffer bb1 = ByteBuffer.allocate(512), bb2 = ByteBuffer.allocate(512);
initSPS.write(bb1);
initPPS.write(bb2);
bb1.flip();
bb2.flip();
return createMOVSampleEntryFromBuffer(bb1, bb2, nalLengthSize);
}
public static SampleEntry createMOVSampleEntryFromBuffer(ByteBuffer sps, ByteBuffer pps, int nalLengthSize) {
return createMOVSampleEntryFromSpsPpsList(Arrays.asList(sps),
Arrays.asList(pps), nalLengthSize);
}
public static boolean iFrame(ByteBuffer _data) {
ByteBuffer data = _data.duplicate();
ByteBuffer segment;
while ((segment = nextNALUnit(data)) != null) {
NALUnitType type = (NALUnit.read(segment)).type;
if (type == NALUnitType.IDR_SLICE || type == NALUnitType.NON_IDR_SLICE) {
unescapeNAL(segment);
BitReader reader = BitReader.createBitReader(segment);
SliceHeader part1 = SliceHeaderReader.readPart1(reader);
return (part1.sliceType == SliceType.I);
}
}
return false;
}
public static boolean isByteBufferIDRSlice(ByteBuffer _data) {
ByteBuffer data = _data.duplicate();
ByteBuffer segment;
while ((segment = nextNALUnit(data)) != null) {
if ((NALUnit.read(segment)).type == NALUnitType.IDR_SLICE)
return true;
}
return false;
}
public static boolean idrSlice(List<ByteBuffer> _data) {
for (ByteBuffer segment : _data) {
if ((NALUnit.read(segment.duplicate())).type == NALUnitType.IDR_SLICE)
return true;
}
return false;
}
public static void saveRawFrame(ByteBuffer data, AvcCBox avcC, File f) throws IOException {
SeekableByteChannel raw = NIOUtils.writableChannel(f);
saveStreamParams(avcC, raw);
raw.write(data.duplicate());
raw.close();
}
public static void saveStreamParams(AvcCBox avcC, SeekableByteChannel raw) throws IOException {
ByteBuffer bb = ByteBuffer.allocate(1024);
for (ByteBuffer byteBuffer : avcC.getSpsList()) {
raw.write(ByteBuffer.wrap(new byte[] { (byte)0, (byte)0, (byte)0, (byte)1, (byte)103 }));
escapeNAL(byteBuffer.duplicate(), bb);
bb.flip();
raw.write(bb);
bb.clear();
}
for (ByteBuffer byteBuffer : avcC.getPpsList()) {
raw.write(ByteBuffer.wrap(new byte[] { (byte)0, (byte)0, (byte)0, (byte)1, (byte)104 }));
escapeNAL(byteBuffer.duplicate(), bb);
bb.flip();
raw.write(bb);
bb.clear();
}
}
public static List<ByteBuffer> splitFrame(ByteBuffer frame) {
ArrayList<ByteBuffer> result = new ArrayList<>();
ByteBuffer segment;
while ((segment = nextNALUnit(frame)) != null)
result.add(segment);
return result;
}
public static ByteBuffer joinNALUnits(List<ByteBuffer> nalUnits) {
int size = 0;
for (ByteBuffer nal : nalUnits)
size += 4 + nal.remaining();
ByteBuffer allocate = ByteBuffer.allocate(size);
joinNALUnitsToBuffer(nalUnits, allocate);
return allocate;
}
public static void joinNALUnitsToBuffer(List<ByteBuffer> nalUnits, ByteBuffer out) {
for (ByteBuffer nal : nalUnits) {
out.putInt(1);
out.put(nal.duplicate());
}
}
public static ByteBuffer getAvcCData(AvcCBox avcC) {
ByteBuffer bb = ByteBuffer.allocate(2048);
avcC.doWrite(bb);
bb.flip();
return bb;
}
public static AvcCBox parseAVCC(VideoSampleEntry vse) {
Box lb = NodeBox.findFirst(vse, Box.class, "avcC");
if (lb instanceof AvcCBox)
return (AvcCBox)lb;
return parseAVCCFromBuffer(((Box.LeafBox)lb).getData().duplicate());
}
public static ByteBuffer saveCodecPrivate(List<ByteBuffer> spsList, List<ByteBuffer> ppsList) {
int totalCodecPrivateSize = 0;
for (ByteBuffer byteBuffer : spsList)
totalCodecPrivateSize += byteBuffer.remaining() + 5;
for (ByteBuffer byteBuffer : ppsList)
totalCodecPrivateSize += byteBuffer.remaining() + 5;
ByteBuffer bb = ByteBuffer.allocate(totalCodecPrivateSize);
for (ByteBuffer byteBuffer : spsList) {
bb.putInt(1);
bb.put((byte)103);
bb.put(byteBuffer.duplicate());
}
for (ByteBuffer byteBuffer : ppsList) {
bb.putInt(1);
bb.put((byte)104);
bb.put(byteBuffer.duplicate());
}
bb.flip();
return bb;
}
public static ByteBuffer avcCToAnnexB(AvcCBox avcC) {
return saveCodecPrivate(avcC.getSpsList(), avcC.getPpsList());
}
public static AvcCBox parseAVCCFromBuffer(ByteBuffer bb) {
return AvcCBox.parseAvcCBox(bb);
}
public static ByteBuffer writeSPS(SeqParameterSet sps, int approxSize) {
ByteBuffer output = ByteBuffer.allocate(approxSize + 8);
sps.write(output);
output.flip();
escapeNALinplace(output);
return output;
}
public static SeqParameterSet readSPS(ByteBuffer data) {
ByteBuffer input = NIOUtils.duplicate(data);
unescapeNAL(input);
SeqParameterSet sps = SeqParameterSet.read(input);
return sps;
}
public static ByteBuffer writePPS(PictureParameterSet pps, int approxSize) {
ByteBuffer output = ByteBuffer.allocate(approxSize + 8);
pps.write(output);
output.flip();
escapeNALinplace(output);
return output;
}
public static PictureParameterSet readPPS(ByteBuffer data) {
ByteBuffer input = NIOUtils.duplicate(data);
unescapeNAL(input);
PictureParameterSet pps = PictureParameterSet.read(input);
return pps;
}
public static PictureParameterSet findPPS(List<PictureParameterSet> ppss, int id) {
for (PictureParameterSet pps : ppss) {
if (pps.picParameterSetId == id)
return pps;
}
return null;
}
public static SeqParameterSet findSPS(List<SeqParameterSet> spss, int id) {
for (SeqParameterSet sps : spss) {
if (sps.seqParameterSetId == id)
return sps;
}
return null;
}
public static abstract class SliceHeaderTweaker {
protected List<SeqParameterSet> sps;
protected List<PictureParameterSet> pps;
protected abstract void tweak(SliceHeader param1SliceHeader);
public SliceHeader run(ByteBuffer is, ByteBuffer os, NALUnit nu) {
ByteBuffer nal = os.duplicate();
H264Utils.unescapeNAL(is);
BitReader reader = BitReader.createBitReader(is);
SliceHeader sh = SliceHeaderReader.readPart1(reader);
PictureParameterSet pp = H264Utils.findPPS(this.pps, sh.picParameterSetId);
return part2(is, os, nu, H264Utils.findSPS(this.sps, pp.picParameterSetId), pp, nal, reader, sh);
}
public SliceHeader runSpsPps(ByteBuffer is, ByteBuffer os, NALUnit nu, SeqParameterSet sps, PictureParameterSet pps) {
ByteBuffer nal = os.duplicate();
H264Utils.unescapeNAL(is);
BitReader reader = BitReader.createBitReader(is);
SliceHeader sh = SliceHeaderReader.readPart1(reader);
return part2(is, os, nu, sps, pps, nal, reader, sh);
}
private SliceHeader part2(ByteBuffer is, ByteBuffer os, NALUnit nu, SeqParameterSet sps, PictureParameterSet pps, ByteBuffer nal, BitReader reader, SliceHeader sh) {
BitWriter writer = new BitWriter(os);
SliceHeaderReader.readPart2(sh, nu, sps, pps, reader);
tweak(sh);
SliceHeaderWriter.write(sh, (nu.type == NALUnitType.IDR_SLICE), nu.nal_ref_idc, writer);
if (pps.entropyCodingModeFlag) {
copyDataCABAC(is, os, reader, writer);
} else {
copyDataCAVLC(is, os, reader, writer);
}
nal.limit(os.position());
H264Utils.escapeNALinplace(nal);
os.position(nal.limit());
return sh;
}
private void copyDataCAVLC(ByteBuffer is, ByteBuffer os, BitReader reader, BitWriter writer) {
int wLeft = 8 - writer.curBit();
if (wLeft != 0)
writer.writeNBit(reader.readNBit(wLeft), wLeft);
writer.flush();
int shift = reader.curBit();
if (shift != 0) {
int mShift = 8 - shift;
int inp = reader.readNBit(mShift);
reader.stop();
while (is.hasRemaining()) {
int out = inp << shift;
inp = is.get() & 0xFF;
out |= inp >> mShift;
os.put((byte)out);
}
os.put((byte)(inp << shift));
} else {
reader.stop();
os.put(is);
}
}
private void copyDataCABAC(ByteBuffer is, ByteBuffer os, BitReader reader, BitWriter writer) {
long bp = (long)reader.curBit();
if (bp != 0L) {
long rem = (long)reader.readNBit(8 - (int)bp);
if ((long)((1 << (int)(8L - bp)) - 1) != rem)
throw new RuntimeException("Invalid CABAC padding");
}
if (writer.curBit() != 0)
writer.writeNBit(255, 8 - writer.curBit());
writer.flush();
reader.stop();
os.put(is);
}
}
public static Size getPicSize(SeqParameterSet sps) {
int w = sps.picWidthInMbsMinus1 + 1 << 4;
int h = SeqParameterSet.getPicHeightInMbs(sps) << 4;
if (sps.frameCroppingFlag) {
w -= sps.frameCropLeftOffset + sps.frameCropRightOffset << sps.chromaFormatIdc.compWidth[1];
h -= sps.frameCropTopOffset + sps.frameCropBottomOffset << sps.chromaFormatIdc.compHeight[1];
}
return new Size(w, h);
}
public static List<SeqParameterSet> readSPSFromBufferList(List<ByteBuffer> spsList) {
List<SeqParameterSet> result = new ArrayList<>();
for (ByteBuffer byteBuffer : spsList)
result.add(readSPS(NIOUtils.duplicate(byteBuffer)));
return result;
}
public static List<PictureParameterSet> readPPSFromBufferList(List<ByteBuffer> ppsList) {
List<PictureParameterSet> result = new ArrayList<>();
for (ByteBuffer byteBuffer : ppsList)
result.add(readPPS(NIOUtils.duplicate(byteBuffer)));
return result;
}
public static List<ByteBuffer> writePPSList(List<PictureParameterSet> allPps) {
List<ByteBuffer> result = new ArrayList<>();
for (PictureParameterSet pps : allPps)
result.add(writePPS(pps, 64));
return result;
}
public static List<ByteBuffer> writeSPSList(List<SeqParameterSet> allSps) {
List<ByteBuffer> result = new ArrayList<>();
for (SeqParameterSet sps : allSps)
result.add(writeSPS(sps, 256));
return result;
}
public static void dumpFrame(FileChannelWrapper ch, SeqParameterSet[] values, PictureParameterSet[] values2, List<ByteBuffer> nalUnits) throws IOException {
for (int j = 0; j < values.length; j++) {
SeqParameterSet sps = values[j];
NIOUtils.writeInt(ch, 1);
NIOUtils.writeByte(ch, (byte)103);
ch.write(writeSPS(sps, 128));
}
for (int i = 0; i < values2.length; i++) {
PictureParameterSet pps = values2[i];
NIOUtils.writeInt(ch, 1);
NIOUtils.writeByte(ch, (byte)104);
ch.write(writePPS(pps, 256));
}
for (ByteBuffer byteBuffer : nalUnits) {
NIOUtils.writeInt(ch, 1);
ch.write(byteBuffer.duplicate());
}
}
public static void toNAL(ByteBuffer codecPrivate, SeqParameterSet sps, PictureParameterSet pps) {
ByteBuffer bb1 = ByteBuffer.allocate(512), bb2 = ByteBuffer.allocate(512);
sps.write(bb1);
pps.write(bb2);
bb1.flip();
bb2.flip();
putNAL(codecPrivate, bb1, 103);
putNAL(codecPrivate, bb2, 104);
}
public static void toNALList(ByteBuffer codecPrivate, List<ByteBuffer> spsList2, List<ByteBuffer> ppsList2) {
for (ByteBuffer byteBuffer : spsList2)
putNAL(codecPrivate, byteBuffer, 103);
for (ByteBuffer byteBuffer : ppsList2)
putNAL(codecPrivate, byteBuffer, 104);
}
private static void putNAL(ByteBuffer codecPrivate, ByteBuffer byteBuffer, int nalType) {
ByteBuffer dst = ByteBuffer.allocate(byteBuffer.remaining() * 2);
escapeNAL(byteBuffer, dst);
dst.flip();
codecPrivate.putInt(1);
codecPrivate.put((byte)nalType);
codecPrivate.put(dst);
}
public static List<ByteBuffer> getRawPPS(ByteBuffer codecPrivate) {
return getRawNALUnitsOfType(codecPrivate, NALUnitType.PPS);
}
public static List<ByteBuffer> getRawSPS(ByteBuffer codecPrivate) {
return getRawNALUnitsOfType(codecPrivate, NALUnitType.SPS);
}
public static List<ByteBuffer> getRawNALUnitsOfType(ByteBuffer codecPrivate, NALUnitType type) {
List<ByteBuffer> result = new ArrayList<>();
for (ByteBuffer bb : splitFrame(codecPrivate.duplicate())) {
NALUnit nu = NALUnit.read(bb);
if (nu.type == type)
result.add(bb);
}
return result;
}
public static class Mv {
public static int mvX(int mv) {
return mv << 18 >> 18;
}
public static int mvY(int mv) {
return mv << 6 >> 20;
}
public static int mvRef(int mv) {
return mv >> 26;
}
public static int packMv(int mvx, int mvy, int r) {
return (r & 0x3F) << 26 | (mvy & 0xFFF) << 14 | mvx & 0x3FFF;
}
public static int mvC(int mv, int comp) {
return (comp == 0) ? mvX(mv) : mvY(mv);
}
}
public static class MvList {
private int[] list;
private static final int NA = H264Utils.Mv.packMv(0, 0, -1);
public MvList(int size) {
this.list = new int[size << 1];
clear();
}
public void clear() {
for (int i = 0; i < this.list.length; i += 2) {
this.list[i + 1] = NA;
this.list[i] = NA;
}
}
public int mv0X(int off) {
return H264Utils.Mv.mvX(this.list[off << 1]);
}
public int mv0Y(int off) {
return H264Utils.Mv.mvY(this.list[off << 1]);
}
public int mv0R(int off) {
return H264Utils.Mv.mvRef(this.list[off << 1]);
}
public int mv1X(int off) {
return H264Utils.Mv.mvX(this.list[(off << 1) + 1]);
}
public int mv1Y(int off) {
return H264Utils.Mv.mvY(this.list[(off << 1) + 1]);
}
public int mv1R(int off) {
return H264Utils.Mv.mvRef(this.list[(off << 1) + 1]);
}
public int getMv(int off, int forward) {
return this.list[(off << 1) + forward];
}
public void setMv(int off, int forward, int mv) {
this.list[(off << 1) + forward] = mv;
}
public void setPair(int off, int mv0, int mv1) {
this.list[off << 1] = mv0;
this.list[(off << 1) + 1] = mv1;
}
public void copyPair(int off, MvList other, int otherOff) {
this.list[off << 1] = other.list[otherOff << 1];
this.list[(off << 1) + 1] = other.list[(otherOff << 1) + 1];
}
}
public static class MvList2D {
private int[] list;
private int stride;
private int width;
private int height;
private static final int NA = H264Utils.Mv.packMv(0, 0, -1);
public MvList2D(int width, int height) {
this.list = new int[(width << 1) * height];
this.stride = width << 1;
this.width = width;
this.height = height;
clear();
}
public void clear() {
for (int i = 0; i < this.list.length; i += 2) {
this.list[i + 1] = NA;
this.list[i] = NA;
}
}
public int mv0X(int offX, int offY) {
return H264Utils.Mv.mvX(this.list[(offX << 1) + this.stride * offY]);
}
public int mv0Y(int offX, int offY) {
return H264Utils.Mv.mvY(this.list[(offX << 1) + this.stride * offY]);
}
public int mv0R(int offX, int offY) {
return H264Utils.Mv.mvRef(this.list[(offX << 1) + this.stride * offY]);
}
public int mv1X(int offX, int offY) {
return H264Utils.Mv.mvX(this.list[(offX << 1) + this.stride * offY + 1]);
}
public int mv1Y(int offX, int offY) {
return H264Utils.Mv.mvY(this.list[(offX << 1) + this.stride * offY + 1]);
}
public int mv1R(int offX, int offY) {
return H264Utils.Mv.mvRef(this.list[(offX << 1) + this.stride * offY + 1]);
}
public int getMv(int offX, int offY, int forward) {
return this.list[(offX << 1) + this.stride * offY + forward];
}
public void setMv(int offX, int offY, int forward, int mv) {
this.list[(offX << 1) + this.stride * offY + forward] = mv;
}
public int getHeight() {
return this.height;
}
public int getWidth() {
return this.width;
}
}
}

View file

@ -0,0 +1,9 @@
package org.jcodec.codecs.h264;
public class H264Utils2 {
public static int golomb2Signed(int val) {
int sign = ((val & 0x1) << 1) - 1;
val = ((val >> 1) + (val & 0x1)) * sign;
return val;
}
}

View file

@ -0,0 +1,69 @@
package org.jcodec.codecs.h264;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.SliceHeader;
public class POCManager {
private int prevPOCMsb;
private int prevPOCLsb;
public int calcPOC(SliceHeader firstSliceHeader, NALUnit firstNu) {
switch (firstSliceHeader.sps.picOrderCntType) {
case 0:
return calcPOC0(firstSliceHeader, firstNu);
case 1:
return calcPOC1(firstSliceHeader, firstNu);
case 2:
return calcPOC2(firstSliceHeader, firstNu);
}
throw new RuntimeException("POC no!!!");
}
private int calcPOC2(SliceHeader firstSliceHeader, NALUnit firstNu) {
return firstSliceHeader.frameNum << 1;
}
private int calcPOC1(SliceHeader firstSliceHeader, NALUnit firstNu) {
return firstSliceHeader.frameNum << 1;
}
private int calcPOC0(SliceHeader firstSliceHeader, NALUnit firstNu) {
int POCMsb;
if (firstNu.type == NALUnitType.IDR_SLICE)
this.prevPOCMsb = this.prevPOCLsb = 0;
int maxPOCLsbDiv2 = 1 << firstSliceHeader.sps.log2MaxPicOrderCntLsbMinus4 + 3, maxPOCLsb = maxPOCLsbDiv2 << 1;
int POCLsb = firstSliceHeader.picOrderCntLsb;
if (POCLsb < this.prevPOCLsb && this.prevPOCLsb - POCLsb >= maxPOCLsbDiv2) {
POCMsb = this.prevPOCMsb + maxPOCLsb;
} else if (POCLsb > this.prevPOCLsb && POCLsb - this.prevPOCLsb > maxPOCLsbDiv2) {
POCMsb = this.prevPOCMsb - maxPOCLsb;
} else {
POCMsb = this.prevPOCMsb;
}
int POC = POCMsb + POCLsb;
if (firstNu.nal_ref_idc > 0)
if (hasMMCO5(firstSliceHeader, firstNu)) {
this.prevPOCMsb = 0;
this.prevPOCLsb = POC;
} else {
this.prevPOCMsb = POCMsb;
this.prevPOCLsb = POCLsb;
}
return POC;
}
private boolean hasMMCO5(SliceHeader firstSliceHeader, NALUnit firstNu) {
if (firstNu.type != NALUnitType.IDR_SLICE && firstSliceHeader.refPicMarkingNonIDR != null) {
RefPicMarking.Instruction[] instructions = firstSliceHeader.refPicMarkingNonIDR.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction instruction = instructions[i];
if (instruction.getType() == RefPicMarking.InstrType.CLEAR)
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,780 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class BlockInterpolator {
private int[] tmp1 = new int[1024];
private int[] tmp2 = new int[1024];
private byte[] tmp3 = new byte[1024];
private LumaInterpolator[] safe = initSafe();
private LumaInterpolator[] unsafe = initUnsafe();
public void getBlockLuma(Picture pic, Picture out, int off, int x, int y, int w, int h) {
int xInd = x & 0x3;
int yInd = y & 0x3;
int xFp = x >> 2;
int yFp = y >> 2;
if (xFp < 2 || yFp < 2 || xFp > pic.getWidth() - w - 5 || yFp > pic.getHeight() - h - 5) {
this.unsafe[(yInd << 2) + xInd].getLuma(pic.getData()[0], pic.getWidth(), pic.getHeight(), out.getPlaneData(0), off,
out.getPlaneWidth(0), xFp, yFp, w, h);
} else {
this.safe[(yInd << 2) + xInd].getLuma(pic.getData()[0], pic.getWidth(), pic.getHeight(), out.getPlaneData(0), off,
out.getPlaneWidth(0), xFp, yFp, w, h);
}
}
public static void getBlockChroma(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int xInd = x & 0x7;
int yInd = y & 0x7;
int xFull = x >> 3;
int yFull = y >> 3;
if (xFull < 0 || xFull > picW - blkW - 1 || yFull < 0 || yFull > picH - blkH - 1) {
if (xInd == 0 && yInd == 0) {
getChroma00Unsafe(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, blkW, blkH);
} else if (yInd == 0) {
getChromaX0Unsafe(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, xInd, blkW, blkH);
} else if (xInd == 0) {
getChroma0XUnsafe(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, yInd, blkW, blkH);
} else {
getChromaXXUnsafe(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, xInd, yInd, blkW, blkH);
}
} else if (xInd == 0 && yInd == 0) {
getChroma00(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, blkW, blkH);
} else if (yInd == 0) {
getChromaX0(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, xInd, blkW, blkH);
} else if (xInd == 0) {
getChroma0X(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, yInd, blkW, blkH);
} else {
getChromaXX(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, xInd, yInd, blkW, blkH);
}
}
static void getLuma00(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
System.arraycopy(pic, off, blk, blkOff, blkW);
off += picW;
blkOff += blkStride;
}
}
static void getLuma00Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = pic[lineStart + MathUtil.clip(x + i, 0, maxW)];
blkOff += blkStride;
}
}
static void getLuma20NoRoundInt(int[] pic, int picW, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
int off1 = -2;
for (int i = 0; i < blkW; i++) {
int a = pic[off + off1] + pic[off + off1 + 5];
int b = pic[off + off1 + 1] + pic[off + off1 + 4];
int c = pic[off + off1 + 2] + pic[off + off1 + 3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
off1++;
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma20NoRound(byte[] pic, int picW, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
int off1 = -2;
for (int i = 0; i < blkW; i++) {
int a = pic[off + off1] + pic[off + off1 + 5];
int b = pic[off + off1 + 1] + pic[off + off1 + 4];
int c = pic[off + off1 + 2] + pic[off + off1 + 3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
off1++;
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma20(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
int off1 = -2;
for (int i = 0; i < blkW; i++) {
int a = pic[off + off1] + pic[off + off1 + 5];
int b = pic[off + off1 + 1] + pic[off + off1 + 4];
int c = pic[off + off1 + 2] + pic[off + off1 + 3];
blk[blkOff + i] = (byte)MathUtil.clip(a + 5 * ((c << 2) - b) + 16 >> 5, -128, 127);
off1++;
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma20UnsafeNoRound(byte[] pic, int picW, int picH, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxW = picW - 1;
int maxH = picH - 1;
for (int i = 0; i < blkW; i++) {
int ipos_m2 = MathUtil.clip(x + i - 2, 0, maxW);
int ipos_m1 = MathUtil.clip(x + i - 1, 0, maxW);
int ipos = MathUtil.clip(x + i, 0, maxW);
int ipos_p1 = MathUtil.clip(x + i + 1, 0, maxW);
int ipos_p2 = MathUtil.clip(x + i + 2, 0, maxW);
int ipos_p3 = MathUtil.clip(x + i + 3, 0, maxW);
int boff = blkOff;
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
int a = pic[lineStart + ipos_m2] + pic[lineStart + ipos_p3];
int b = pic[lineStart + ipos_m1] + pic[lineStart + ipos_p2];
int c = pic[lineStart + ipos] + pic[lineStart + ipos_p1];
blk[boff + i] = a + 5 * ((c << 2) - b);
boff += blkStride;
}
}
}
void getLuma20Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20UnsafeNoRound(pic, picW, picH, this.tmp1, blkOff, blkStride, x, y, blkW, blkH);
for (int i = 0; i < blkW; i++) {
int boff = blkOff;
for (int j = 0; j < blkH; j++) {
blk[boff + i] = (byte)MathUtil.clip(this.tmp1[boff + i] + 16 >> 5, -128, 127);
boff += blkStride;
}
}
}
static void getLuma02NoRoundInt(int[] pic, int picW, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = (y - 2) * picW + x, picWx2 = picW + picW, picWx3 = picWx2 + picW, picWx4 = picWx3 + picW, picWx5 = picWx4 + picW;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int a = pic[off + i] + pic[off + i + picWx5];
int b = pic[off + i + picW] + pic[off + i + picWx4];
int c = pic[off + i + picWx2] + pic[off + i + picWx3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma02NoRound(byte[] pic, int picW, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = (y - 2) * picW + x, picWx2 = picW + picW, picWx3 = picWx2 + picW, picWx4 = picWx3 + picW, picWx5 = picWx4 + picW;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int a = pic[off + i] + pic[off + i + picWx5];
int b = pic[off + i + picW] + pic[off + i + picWx4];
int c = pic[off + i + picWx2] + pic[off + i + picWx3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma02(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = (y - 2) * picW + x, picWx2 = picW + picW, picWx3 = picWx2 + picW, picWx4 = picWx3 + picW, picWx5 = picWx4 + picW;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int a = pic[off + i] + pic[off + i + picWx5];
int b = pic[off + i + picW] + pic[off + i + picWx4];
int c = pic[off + i + picWx2] + pic[off + i + picWx3];
blk[blkOff + i] = (byte)MathUtil.clip(a + 5 * ((c << 2) - b) + 16 >> 5, -128, 127);
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma02UnsafeNoRound(byte[] pic, int picW, int picH, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
for (int j = 0; j < blkH; j++) {
int offP0 = MathUtil.clip(y + j - 2, 0, maxH) * picW;
int offP1 = MathUtil.clip(y + j - 1, 0, maxH) * picW;
int offP2 = MathUtil.clip(y + j, 0, maxH) * picW;
int offP3 = MathUtil.clip(y + j + 1, 0, maxH) * picW;
int offP4 = MathUtil.clip(y + j + 2, 0, maxH) * picW;
int offP5 = MathUtil.clip(y + j + 3, 0, maxH) * picW;
for (int i = 0; i < blkW; i++) {
int pres_x = MathUtil.clip(x + i, 0, maxW);
int a = pic[pres_x + offP0] + pic[pres_x + offP5];
int b = pic[pres_x + offP1] + pic[pres_x + offP4];
int c = pic[pres_x + offP2] + pic[pres_x + offP3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
}
blkOff += blkStride;
}
}
void getLuma02Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma02UnsafeNoRound(pic, picW, picH, this.tmp1, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)MathUtil.clip(this.tmp1[blkOff + i] + 16 >> 5, -128, 127);
blkOff += blkStride;
}
}
static void getLuma10(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[off + i] + 1 >> 1);
off += picW;
blkOff += blkStride;
}
}
void getLuma10Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
getLuma20Unsafe(pic, picW, picH, blk, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[lineStart + MathUtil.clip(x + i, 0, maxW)] + 1 >> 1);
blkOff += blkStride;
}
}
static void getLuma30(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(pic[off + i + 1] + blk[blkOff + i] + 1 >> 1);
off += picW;
blkOff += blkStride;
}
}
void getLuma30Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
getLuma20Unsafe(pic, picW, picH, blk, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[lineStart + MathUtil.clip(x + i + 1, 0, maxW)] + 1 >> 1);
blkOff += blkStride;
}
}
static void getLuma01(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma02(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[off + i] + 1 >> 1);
off += picW;
blkOff += blkStride;
}
}
void getLuma01Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
getLuma02Unsafe(pic, picW, picH, blk, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(y + j, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[lineStart + MathUtil.clip(x + i, 0, maxW)] + 1 >> 1);
blkOff += blkStride;
}
}
static void getLuma03(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma02(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[off + i + picW] + 1 >> 1);
off += picW;
blkOff += blkStride;
}
}
void getLuma03Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
getLuma02Unsafe(pic, picW, picH, blk, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(y + j + 1, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[lineStart + MathUtil.clip(x + i, 0, maxW)] + 1 >> 1);
blkOff += blkStride;
}
}
void getLuma21(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20NoRound(pic, picW, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
int off = blkW << 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += blkW;
}
}
void getLuma21Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
int off = blkW << 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += blkW;
}
}
void getLuma22(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20NoRound(pic, picW, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
blkOff += blkStride;
}
}
void getLuma22Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
blkOff += blkStride;
}
}
void getLuma23(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20NoRound(pic, picW, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
int off = blkW << 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i + blkW] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += blkW;
}
}
void getLuma23Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
int off = blkW << 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i + blkW] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += blkW;
}
}
void getLuma12(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int tmpW = blkW + 7;
getLuma02NoRound(pic, picW, this.tmp1, 0, tmpW, x - 2, y, tmpW, blkH);
getLuma20NoRoundInt(this.tmp1, tmpW, this.tmp2, blkOff, blkStride, 2, 0, blkW, blkH);
int off = 2;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += tmpW;
}
}
void getLuma12Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int tmpW = blkW + 7;
getLuma02UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, tmpW, x - 2, y, tmpW, blkH);
getLuma20NoRoundInt(this.tmp1, tmpW, this.tmp2, blkOff, blkStride, 2, 0, blkW, blkH);
int off = 2;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += tmpW;
}
}
void getLuma32(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int tmpW = blkW + 7;
getLuma02NoRound(pic, picW, this.tmp1, 0, tmpW, x - 2, y, tmpW, blkH);
getLuma20NoRoundInt(this.tmp1, tmpW, this.tmp2, blkOff, blkStride, 2, 0, blkW, blkH);
int off = 2;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i + 1] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += tmpW;
}
}
void getLuma32Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int tmpW = blkW + 7;
getLuma02UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, tmpW, x - 2, y, tmpW, blkH);
getLuma20NoRoundInt(this.tmp1, tmpW, this.tmp2, blkOff, blkStride, 2, 0, blkW, blkH);
int off = 2;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i + 1] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += tmpW;
}
}
void getLuma33(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y + 1, blkW, blkH);
getLuma02(pic, picW, this.tmp3, 0, blkW, x + 1, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma33Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20Unsafe(pic, picW, imgH, blk, blkOff, blkStride, x, y + 1, blkW, blkH);
getLuma02Unsafe(pic, picW, imgH, this.tmp3, 0, blkW, x + 1, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma11(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
getLuma02(pic, picW, this.tmp3, 0, blkW, x, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma11Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20Unsafe(pic, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
getLuma02Unsafe(pic, picW, imgH, this.tmp3, 0, blkW, x, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma13(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y + 1, blkW, blkH);
getLuma02(pic, picW, this.tmp3, 0, blkW, x, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma13Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20Unsafe(pic, picW, imgH, blk, blkOff, blkStride, x, y + 1, blkW, blkH);
getLuma02Unsafe(pic, picW, imgH, this.tmp3, 0, blkW, x, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma31(byte[] pels, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
getLuma02(pels, picW, this.tmp3, 0, blkW, x + 1, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma31Unsafe(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
getLuma02Unsafe(pels, picW, imgH, this.tmp3, 0, blkW, x + 1, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
private static void merge(byte[] first, byte[] second, int blkOff, int blkStride, int blkW, int blkH) {
int tOff = 0;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
first[blkOff + i] = (byte)(first[blkOff + i] + second[tOff + i] + 1 >> 1);
blkOff += blkStride;
tOff += blkW;
}
}
private static void getChroma00(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
System.arraycopy(pic, off, blk, blkOff, blkW);
off += picW;
blkOff += blkStride;
}
}
private static void getChroma00Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = pic[lineStart + MathUtil.clip(x + i, 0, maxW)];
blkOff += blkStride;
}
}
private static void getChroma0X(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracY, int blkW, int blkH) {
int w00 = fullY * picW + fullX;
int w01 = w00 + ((fullY < picH - 1) ? picW : 0);
int eMy = 8 - fracY;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(eMy * pels[w00 + i] + fracY * pels[w01 + i] + 4 >> 3);
w00 += picW;
w01 += picW;
blkOff += blkStride;
}
}
private static void getChroma0XUnsafe(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracY, int blkW, int blkH) {
int maxW = picW - 1;
int maxH = picH - 1;
int eMy = 8 - fracY;
for (int j = 0; j < blkH; j++) {
int off00 = MathUtil.clip(fullY + j, 0, maxH) * picW;
int off01 = MathUtil.clip(fullY + j + 1, 0, maxH) * picW;
for (int i = 0; i < blkW; i++) {
int w00 = MathUtil.clip(fullX + i, 0, maxW) + off00;
int w01 = MathUtil.clip(fullX + i, 0, maxW) + off01;
blk[blkOff + i] = (byte)(eMy * pels[w00] + fracY * pels[w01] + 4 >> 3);
}
blkOff += blkStride;
}
}
private static void getChromaX0(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracX, int blkW, int blkH) {
int w00 = fullY * picW + fullX;
int w10 = w00 + ((fullX < picW - 1) ? 1 : 0);
int eMx = 8 - fracX;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(eMx * pels[w00 + i] + fracX * pels[w10 + i] + 4 >> 3);
w00 += picW;
w10 += picW;
blkOff += blkStride;
}
}
private static void getChromaX0Unsafe(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracX, int blkW, int blkH) {
int eMx = 8 - fracX;
int maxW = picW - 1;
int maxH = picH - 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int w00 = MathUtil.clip(fullY + j, 0, maxH) * picW + MathUtil.clip(fullX + i, 0, maxW);
int w10 = MathUtil.clip(fullY + j, 0, maxH) * picW + MathUtil.clip(fullX + i + 1, 0, maxW);
blk[blkOff + i] = (byte)(eMx * pels[w00] + fracX * pels[w10] + 4 >> 3);
}
blkOff += blkStride;
}
}
private static void getChromaXX(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracX, int fracY, int blkW, int blkH) {
int w00 = fullY * picW + fullX;
int w01 = w00 + ((fullY < picH - 1) ? picW : 0);
int w10 = w00 + ((fullX < picW - 1) ? 1 : 0);
int w11 = w10 + w01 - w00;
int eMx = 8 - fracX;
int eMy = 8 - fracY;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(eMx * eMy * pels[w00 + i] + fracX * eMy * pels[w10 + i] + eMx * fracY * pels[w01 + i] + fracX * fracY * pels[w11 + i] + 32 >> 6);
blkOff += blkStride;
w00 += picW;
w01 += picW;
w10 += picW;
w11 += picW;
}
}
private static void getChromaXXUnsafe(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracX, int fracY, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
int eMx = 8 - fracX;
int eMy = 8 - fracY;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int w00 = MathUtil.clip(fullY + j, 0, maxH) * picW + MathUtil.clip(fullX + i, 0, maxW);
int w01 = MathUtil.clip(fullY + j + 1, 0, maxH) * picW + MathUtil.clip(fullX + i, 0, maxW);
int w10 = MathUtil.clip(fullY + j, 0, maxH) * picW + MathUtil.clip(fullX + i + 1, 0, maxW);
int w11 = MathUtil.clip(fullY + j + 1, 0, maxH) * picW + MathUtil.clip(fullX + i + 1, 0, maxW);
blk[blkOff + i] = (byte)(eMx * eMy * pels[w00] + fracX * eMy * pels[w10] + eMx * fracY * pels[w01] + fracX * fracY * pels[w11] + 32 >> 6);
}
blkOff += blkStride;
}
}
private LumaInterpolator[] initSafe() {
final BlockInterpolator self = this;
return new LumaInterpolator[] {
new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma00(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma10(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma20(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma30(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma01(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma11(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma21(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma31(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma02(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma12(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
},
new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma22(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma32(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma03(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma13(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma23(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma33(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
} };
}
private LumaInterpolator[] initUnsafe() {
final BlockInterpolator self = this;
return new LumaInterpolator[] {
new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma00Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma10Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma20Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma30Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma01Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma11Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma21Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma31Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma02Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma12Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
},
new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma22Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma32Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma03Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma13Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma23Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma33Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
} };
}
private static interface LumaInterpolator {
void getLuma(byte[] param1ArrayOfbyte1, int param1Int1, int param1Int2, byte[] param1ArrayOfbyte2, int param1Int3, int param1Int4, int param1Int5, int param1Int6, int param1Int7, int param1Int8);
}
}

View file

@ -0,0 +1,835 @@
package org.jcodec.codecs.h264.decode;
public class CABACContst {
public static int[] cabac_context_init_I_A = new int[] {
20, 2, 3, 20, 2, 3, -28, -23, -6, -1,
7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, -9, 4, 0, -7, 13, 3,
0, 1, 0, -17, -13, 0, -7, -21, -27, -31,
-24, -18, -27, -21, -30, -17, -12, -16, -11, -12,
-2, -15, -13, -3, -8, -10, -30, -1, -6, -7,
-20, -4, -5, -7, -22, -7, -11, -3, -5, -4,
-4, -12, -7, -7, 8, 5, -2, 1, 0, -2,
1, 7, 10, 0, 11, 1, 0, 5, 31, 1,
7, 28, 16, 14, -13, -15, -13, -13, -12, -10,
-16, -10, -7, -13, -19, 1, 0, -5, 18, -8,
-15, 0, -4, 2, -11, -3, 15, -13, 0, 0,
21, -15, 9, 16, 0, 12, 24, 15, 8, 13,
15, 13, 10, 12, 6, 20, 15, 4, 1, 0,
7, 12, 11, 15, 11, 13, 16, 12, 10, 30,
18, 10, 17, 17, 0, 26, 22, 26, 30, 28,
33, 37, 33, 40, 38, 33, 40, 41, 38, 41,
30, 27, 26, 37, 35, 38, 38, 37, 38, 42,
35, 39, 14, 27, 21, 12, 2, -3, -6, -5,
-3, -2, 0, 1, -2, -1, -9, -5, -5, -3,
-2, 0, -16, -8, -10, -6, -10, -12, -15, -10,
-6, -4, -12, -8, -7, -9, -17, -11, -20, -11,
-6, -4, -13, -13, -11, -19, -8, -5, -4, -2,
-3, -13, -10, -12, -13, -14, 0, -6, -6, -8,
0, -1, 0, -2, -2, -5, -3, -4, -9, -1,
0, 3, 10, -7, 15, 14, 16, 12, 1, 20,
18, 5, 1, 10, 17, 9, -12, -11, -16, -7,
-8, -7, -9, -13, 4, -3, -3, -6, 10, -1,
-1, -7, -14, 2, 0, -5, 0, -11, 1, 0,
-14, 3, 4, -1, -13, 11, 5, 12, 15, 6,
7, 12, 18, 13, 13, 15, 12, 13, 15, 14,
14, 17, 17, 24, 21, 25, 31, 22, 19, 14,
10, 7, -2, -4, -3, 9, -12, 36, 36, 32,
37, 44, 34, 34, 40, 33, 35, 33, 38, 33,
23, 13, 29, 26, 22, 31, 35, 34, 34, 36,
34, 32, 35, 34, 39, 30, 34, 29, 19, 31,
31, 25, -17, -20, -18, -11, -15, -14, -26, -15,
-14, 0, -14, -24, -23, -24, -11, 23, 26, 40,
49, 44, 45, 44, 33, 19, -3, -1, 1, 1,
0, -2, 0, 1, 0, -9, -14, -13, -15, -12,
-18, -10, -9, -14, -10, -10, -10, -5, -9, -5,
2, 21, 24, 28, 28, 29, 29, 35, 29, 14,
-17, -12, -16, -11, -12, -2, -15, -13, -3, -8,
-10, -30, -17, -12, -16, -11, -12, -2, -15, -13,
-3, -8, -10, -30, -7, -11, -3, -5, -4, -4,
-12, -7, -7, 8, 5, -2, 1, 0, -2, 1,
7, 10, 0, 11, 1, 0, 5, 31, 1, 7,
28, 16, 14, -13, -15, -13, -13, -12, -10, -16,
-10, -7, -13, -19, 1, 0, -5, 18, -7, -11,
-3, -5, -4, -4, -12, -7, -7, 8, 5, -2,
1, 0, -2, 1, 7, 10, 0, 11, 1, 0,
5, 31, 1, 7, 28, 16, 14, -13, -15, -13,
-13, -12, -10, -16, -10, -7, -13, -19, 1, 0,
-5, 18, 24, 15, 8, 13, 15, 13, 10, 12,
6, 20, 15, 4, 1, 0, 7, 12, 11, 15,
11, 13, 16, 12, 10, 30, 18, 10, 17, 17,
0, 26, 22, 26, 30, 28, 33, 37, 33, 40,
38, 33, 40, 41, 38, 41, 24, 15, 8, 13,
15, 13, 10, 12, 6, 20, 15, 4, 1, 0,
7, 12, 11, 15, 11, 13, 16, 12, 10, 30,
18, 10, 17, 17, 0, 26, 22, 26, 30, 28,
33, 37, 33, 40, 38, 33, 40, 41, 38, 41,
-17, -20, -18, -11, -15, -14, -26, -15, -14, 0,
-14, -24, -23, -24, -11, -14, -13, -15, -12, -18,
-10, -9, -14, -10, -10, -10, -5, -9, -5, 2,
23, 26, 40, 49, 44, 45, 44, 33, 19, 21,
24, 28, 28, 29, 29, 35, 29, 14, -3, -1,
1, 1, 0, -2, 0, 1, 0, -9, -17, -20,
-18, -11, -15, -14, -26, -15, -14, 0, -14, -24,
-23, -24, -11, -14, -13, -15, -12, -18, -10, -9,
-14, -10, -10, -10, -5, -9, -5, 2, 23, 26,
40, 49, 44, 45, 44, 33, 19, 21, 24, 28,
28, 29, 29, 35, 29, 14, -3, -1, 1, 1,
0, -2, 0, 1, 0, -9, -6, -6, -8, 0,
-1, 0, -2, -2, -5, -3, -4, -9, -1, 0,
3, 10, -7, 15, 14, 16, 12, 1, 20, 18,
5, 1, 10, 17, 9, -12, -11, -16, -7, -8,
-7, -9, -13, 4, -3, -3, -6, 10, -1, -1,
-6, -6, -8, 0, -1, 0, -2, -2, -5, -3,
-4, -9, -1, 0, 3, 10, -7, 15, 14, 16,
12, 1, 20, 18, 5, 1, 10, 17, 9, -12,
-11, -16, -7, -8, -7, -9, -13, 4, -3, -3,
-6, 10, -1, -1, 15, 6, 7, 12, 18, 13,
13, 15, 12, 13, 15, 14, 14, 17, 17, 24,
21, 25, 31, 22, 19, 14, 10, 7, -2, -4,
-3, 9, -12, 36, 36, 32, 37, 44, 34, 34,
40, 33, 35, 33, 38, 33, 23, 13, 15, 6,
7, 12, 18, 13, 13, 15, 12, 13, 15, 14,
14, 17, 17, 24, 21, 25, 31, 22, 19, 14,
10, 7, -2, -4, -3, 9, -12, 36, 36, 32,
37, 44, 34, 34, 40, 33, 35, 33, 38, 33,
23, 13, -3, -6, -5, -3, -2, 0, 1, -2,
-1, -9, -5, -5, -3, -2, 0, -16, -8, -10,
-6, -10, -12, -15, -10, -6, -4, -12, -8, -7,
-9, -17, -3, -6, -5, -3, -2, 0, 1, -2,
-1, -9, -5, -5, -3, -2, 0, -16, -8, -10,
-6, -10, -12, -15, -10, -6, -4, -12, -8, -7,
-9, -17, -3, -8, -10, -30, -3, -8, -10, -30,
-3, -8, -10, -30 };
public static int[] cabac_context_init_I_B = new int[] {
-15, 54, 74, -15, 54, 74, 127, 104, 53, 54,
51, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41, 63, 63, 63, 83, 86, 97, 72, 41, 62,
11, 55, 69, 127, 102, 82, 74, 107, 127, 127,
127, 95, 127, 114, 127, 123, 115, 122, 115, 63,
68, 84, 104, 70, 93, 90, 127, 74, 97, 91,
127, 56, 82, 76, 125, 93, 87, 77, 71, 63,
68, 84, 62, 65, 61, 56, 66, 64, 61, 78,
50, 52, 35, 44, 38, 45, 46, 44, 17, 51,
50, 19, 33, 62, 108, 100, 101, 91, 94, 88,
84, 86, 83, 87, 94, 70, 72, 74, 59, 102,
100, 95, 75, 72, 75, 71, 46, 69, 62, 65,
37, 72, 57, 54, 62, 72, 0, 9, 25, 18,
9, 19, 37, 18, 29, 33, 30, 45, 58, 62,
61, 38, 45, 39, 42, 44, 45, 41, 49, 34,
42, 55, 51, 46, 89, -19, -17, -17, -25, -20,
-23, -27, -23, -28, -17, -11, -15, -6, 1, 17,
-6, 3, 22, -16, -4, -8, -3, 3, 5, 0,
16, 22, 48, 37, 60, 68, 97, 71, 42, 50,
54, 62, 58, 63, 72, 74, 91, 67, 27, 39,
44, 46, 64, 68, 78, 77, 86, 92, 55, 60,
62, 65, 73, 76, 80, 88, 110, 97, 84, 79,
73, 74, 86, 96, 97, 117, 78, 33, 48, 53,
62, 71, 79, 86, 90, 97, 0, 93, 84, 79,
66, 71, 62, 60, 59, 75, 62, 58, 66, 79,
71, 68, 44, 62, 36, 40, 27, 29, 44, 36,
32, 42, 48, 62, 46, 64, 104, 97, 96, 88,
85, 85, 85, 88, 66, 77, 76, 76, 58, 76,
83, 99, 95, 95, 76, 74, 70, 75, 68, 65,
73, 62, 62, 68, 75, 55, 64, 70, 6, 19,
16, 14, 13, 11, 15, 16, 23, 23, 20, 26,
44, 40, 47, 17, 21, 22, 27, 29, 35, 50,
57, 63, 77, 82, 94, 69, 109, -35, -34, -26,
-30, -32, -18, -15, -15, -7, -5, 0, 2, 13,
35, 58, -3, 0, 30, -7, -15, -3, 3, -1,
5, 11, 5, 12, 11, 29, 26, 39, 66, 21,
31, 50, 120, 112, 114, 85, 92, 89, 71, 81,
80, 68, 70, 56, 68, 50, 74, -13, -13, -15,
-14, 3, 6, 34, 54, 82, 75, 23, 34, 43,
54, 55, 61, 64, 68, 92, 106, 97, 90, 90,
88, 73, 79, 86, 73, 70, 69, 66, 64, 58,
59, -10, -11, -8, -1, 3, 9, 20, 36, 67,
123, 115, 122, 115, 63, 68, 84, 104, 70, 93,
90, 127, 123, 115, 122, 115, 63, 68, 84, 104,
70, 93, 90, 127, 93, 87, 77, 71, 63, 68,
84, 62, 65, 61, 56, 66, 64, 61, 78, 50,
52, 35, 44, 38, 45, 46, 44, 17, 51, 50,
19, 33, 62, 108, 100, 101, 91, 94, 88, 84,
86, 83, 87, 94, 70, 72, 74, 59, 93, 87,
77, 71, 63, 68, 84, 62, 65, 61, 56, 66,
64, 61, 78, 50, 52, 35, 44, 38, 45, 46,
44, 17, 51, 50, 19, 33, 62, 108, 100, 101,
91, 94, 88, 84, 86, 83, 87, 94, 70, 72,
74, 59, 0, 9, 25, 18, 9, 19, 37, 18,
29, 33, 30, 45, 58, 62, 61, 38, 45, 39,
42, 44, 45, 41, 49, 34, 42, 55, 51, 46,
89, -19, -17, -17, -25, -20, -23, -27, -23, -28,
-17, -11, -15, -6, 1, 17, 0, 9, 25, 18,
9, 19, 37, 18, 29, 33, 30, 45, 58, 62,
61, 38, 45, 39, 42, 44, 45, 41, 49, 34,
42, 55, 51, 46, 89, -19, -17, -17, -25, -20,
-23, -27, -23, -28, -17, -11, -15, -6, 1, 17,
120, 112, 114, 85, 92, 89, 71, 81, 80, 68,
70, 56, 68, 50, 74, 106, 97, 90, 90, 88,
73, 79, 86, 73, 70, 69, 66, 64, 58, 59,
-13, -13, -15, -14, 3, 6, 34, 54, 82, -10,
-11, -8, -1, 3, 9, 20, 36, 67, 75, 23,
34, 43, 54, 55, 61, 64, 68, 92, 120, 112,
114, 85, 92, 89, 71, 81, 80, 68, 70, 56,
68, 50, 74, 106, 97, 90, 90, 88, 73, 79,
86, 73, 70, 69, 66, 64, 58, 59, -13, -13,
-15, -14, 3, 6, 34, 54, 82, -10, -11, -8,
-1, 3, 9, 20, 36, 67, 75, 23, 34, 43,
54, 55, 61, 64, 68, 92, 93, 84, 79, 66,
71, 62, 60, 59, 75, 62, 58, 66, 79, 71,
68, 44, 62, 36, 40, 27, 29, 44, 36, 32,
42, 48, 62, 46, 64, 104, 97, 96, 88, 85,
85, 85, 88, 66, 77, 76, 76, 58, 76, 83,
93, 84, 79, 66, 71, 62, 60, 59, 75, 62,
58, 66, 79, 71, 68, 44, 62, 36, 40, 27,
29, 44, 36, 32, 42, 48, 62, 46, 64, 104,
97, 96, 88, 85, 85, 85, 88, 66, 77, 76,
76, 58, 76, 83, 6, 19, 16, 14, 13, 11,
15, 16, 23, 23, 20, 26, 44, 40, 47, 17,
21, 22, 27, 29, 35, 50, 57, 63, 77, 82,
94, 69, 109, -35, -34, -26, -30, -32, -18, -15,
-15, -7, -5, 0, 2, 13, 35, 58, 6, 19,
16, 14, 13, 11, 15, 16, 23, 23, 20, 26,
44, 40, 47, 17, 21, 22, 27, 29, 35, 50,
57, 63, 77, 82, 94, 69, 109, -35, -34, -26,
-30, -32, -18, -15, -15, -7, -5, 0, 2, 13,
35, 58, 71, 42, 50, 54, 62, 58, 63, 72,
74, 91, 67, 27, 39, 44, 46, 64, 68, 78,
77, 86, 92, 55, 60, 62, 65, 73, 76, 80,
88, 110, 71, 42, 50, 54, 62, 58, 63, 72,
74, 91, 67, 27, 39, 44, 46, 64, 68, 78,
77, 86, 92, 55, 60, 62, 65, 73, 76, 80,
88, 110, 70, 93, 90, 127, 70, 93, 90, 127,
70, 93, 90, 127 };
public static int[][] cabac_context_init_PB_A = new int[][] { new int[] {
20, 2, 3, 20, 2, 3, -28, -23, -6, -1,
7, 23, 23, 21, 1, 0, -37, 5, -13, -11,
1, 12, -4, 17, 18, 9, 29, 26, 16, 9,
-46, -20, 1, -13, -11, 1, -6, -17, -6, 9,
-3, -6, -11, 6, 7, -5, 2, 0, -3, -10,
5, 4, -3, 0, -7, -5, -4, -5, -7, 1,
0, 0, 0, 0, -9, 4, 0, -7, 13, 3,
0, -4, -3, -27, -28, -25, -23, -28, -20, -16,
-22, -21, -18, -13, -29, -7, -5, -7, -13, -3,
-1, -1, -9, -3, -9, -8, -23, 5, 6, 6,
6, -1, 0, -4, -8, -2, -6, -1, -7, 2,
5, -3, 1, 6, -4, 1, -4, 0, 2, -2,
11, 4, 1, 11, 18, 12, 13, 13, -10, -7,
-2, 13, 9, -7, 9, 2, 5, -2, 0, 0,
-13, -5, -1, 4, -6, 4, 14, 4, 13, 3,
1, 9, 7, 16, 5, 4, 11, -5, -1, 0,
22, 5, 14, -1, 0, 9, 11, 2, 3, 0,
0, 2, 2, 0, 4, 2, 6, 0, 3, 2,
4, 6, 6, 7, 6, 6, 11, 14, 8, -1,
7, -3, 15, 22, -1, 25, 30, 28, 28, 32,
34, 30, 30, 32, 31, 26, 26, 37, 28, 17,
1, 5, 9, 16, 18, 18, 22, 24, 23, 18,
20, 11, 9, 9, -1, -2, -9, -6, -2, 0,
0, -3, -2, -4, -4, -8, -17, -9, 3, 0,
0, 0, -6, -7, -12, -11, -30, 1, -3, -1,
1, 2, -6, 0, 0, -3, -10, 0, -4, 5,
7, 1, -2, -3, -3, -11, 0, 8, 10, 14,
13, 2, 0, -3, -6, -8, 0, -13, -16, -10,
-21, -18, -14, -22, -21, -18, -21, -23, -26, -10,
-12, -5, -9, -22, -5, 9, -4, -10, -1, 7,
9, 5, 12, 15, 18, 17, 10, 7, -1, 7,
8, 9, 6, 2, 13, 10, 6, 5, 13, 4,
6, -2, -2, 6, 10, 9, 12, 3, 14, 10,
-3, 13, 17, 7, 7, 13, 10, 26, 14, 11,
9, 18, 21, 23, 32, 32, 34, 39, 42, 41,
46, 38, 21, 45, 53, 48, 65, 43, 39, 30,
18, 20, 0, -14, -5, -19, -35, 27, 28, 31,
27, 34, 30, 24, 33, 22, 26, 21, 26, 23,
16, 14, 8, 6, 17, 21, 23, 26, 27, 28,
28, 23, 24, 28, 23, 19, 22, 22, 11, 12,
11, 14, -4, -7, -5, -9, -8, -10, -19, -12,
-16, -15, -20, -19, -16, -22, -20, 9, 26, 33,
39, 41, 45, 49, 45, 36, -6, -7, -7, -8,
-5, -12, -6, -5, -8, -8, -5, -6, -10, -7,
-17, -18, -4, -10, -9, -9, -1, -8, -14, 0,
2, 21, 33, 39, 46, 51, 60, 61, 55, 42,
-7, -5, -7, -13, -3, -1, -1, -9, -3, -9,
-8, -23, -7, -5, -7, -13, -3, -1, -1, -9,
-3, -9, -8, -23, -2, -6, -1, -7, 2, 5,
-3, 1, 6, -4, 1, -4, 0, 2, -2, 11,
4, 1, 11, 18, 12, 13, 13, -10, -7, -2,
13, 9, -7, 9, 2, 5, -2, 0, 0, -13,
-5, -1, 4, -6, 4, 14, 4, 13, -2, -6,
-1, -7, 2, 5, -3, 1, 6, -4, 1, -4,
0, 2, -2, 11, 4, 1, 11, 18, 12, 13,
13, -10, -7, -2, 13, 9, -7, 9, 2, 5,
-2, 0, 0, -13, -5, -1, 4, -6, 4, 14,
4, 13, 11, 2, 3, 0, 0, 2, 2, 0,
4, 2, 6, 0, 3, 2, 4, 6, 6, 7,
6, 6, 11, 14, 8, -1, 7, -3, 15, 22,
-1, 25, 30, 28, 28, 32, 34, 30, 30, 32,
31, 26, 26, 37, 28, 17, 11, 2, 3, 0,
0, 2, 2, 0, 4, 2, 6, 0, 3, 2,
4, 6, 6, 7, 6, 6, 11, 14, 8, -1,
7, -3, 15, 22, -1, 25, 30, 28, 28, 32,
34, 30, 30, 32, 31, 26, 26, 37, 28, 17,
-4, -7, -5, -9, -8, -10, -19, -12, -16, -15,
-20, -19, -16, -22, -20, -5, -6, -10, -7, -17,
-18, -4, -10, -9, -9, -1, -8, -14, 0, 2,
9, 26, 33, 39, 41, 45, 49, 45, 36, 21,
33, 39, 46, 51, 60, 61, 55, 42, -6, -7,
-7, -8, -5, -12, -6, -5, -8, -8, -4, -7,
-5, -9, -8, -10, -19, -12, -16, -15, -20, -19,
-16, -22, -20, -5, -6, -10, -7, -17, -18, -4,
-10, -9, -9, -1, -8, -14, 0, 2, 9, 26,
33, 39, 41, 45, 49, 45, 36, 21, 33, 39,
46, 51, 60, 61, 55, 42, -6, -7, -7, -8,
-5, -12, -6, -5, -8, -8, -13, -16, -10, -21,
-18, -14, -22, -21, -18, -21, -23, -26, -10, -12,
-5, -9, -22, -5, 9, -4, -10, -1, 7, 9,
5, 12, 15, 18, 17, 10, 7, -1, 7, 8,
9, 6, 2, 13, 10, 6, 5, 13, 4, 6,
-13, -16, -10, -21, -18, -14, -22, -21, -18, -21,
-23, -26, -10, -12, -5, -9, -22, -5, 9, -4,
-10, -1, 7, 9, 5, 12, 15, 18, 17, 10,
7, -1, 7, 8, 9, 6, 2, 13, 10, 6,
5, 13, 4, 6, 14, 11, 9, 18, 21, 23,
32, 32, 34, 39, 42, 41, 46, 38, 21, 45,
53, 48, 65, 43, 39, 30, 18, 20, 0, -14,
-5, -19, -35, 27, 28, 31, 27, 34, 30, 24,
33, 22, 26, 21, 26, 23, 16, 14, 14, 11,
9, 18, 21, 23, 32, 32, 34, 39, 42, 41,
46, 38, 21, 45, 53, 48, 65, 43, 39, 30,
18, 20, 0, -14, -5, -19, -35, 27, 28, 31,
27, 34, 30, 24, 33, 22, 26, 21, 26, 23,
16, 14, -6, -2, 0, 0, -3, -2, -4, -4,
-8, -17, -9, 3, 0, 0, 0, -6, -7, -12,
-11, -30, 1, -3, -1, 1, 2, -6, 0, 0,
-3, -10, -6, -2, 0, 0, -3, -2, -4, -4,
-8, -17, -9, 3, 0, 0, 0, -6, -7, -12,
-11, -30, 1, -3, -1, 1, 2, -6, 0, 0,
-3, -10, -3, -9, -8, -23, -3, -9, -8, -23,
-3, -9, -8, -23 }, new int[] {
20, 2, 3, 20, 2, 3, -28, -23, -6, -1,
7, 22, 34, 16, -2, 4, -29, 2, -6, -13,
5, 9, -3, 10, 26, 19, 40, 57, 41, 26,
-45, -15, -4, -6, -13, 5, 6, -13, 0, 8,
-2, -5, -10, 2, 2, -3, -3, 1, -3, -6,
0, -3, -7, -5, -1, -1, 1, -2, -5, 0,
0, 0, 0, 0, -9, 4, 0, -7, 13, 3,
13, 7, 2, -39, -18, -17, -26, -35, -24, -23,
-27, -24, -21, -18, -36, 0, -5, -7, -4, 0,
0, -15, -35, -2, -12, -9, -31, 3, 7, 7,
8, -3, 0, -7, -9, -13, -13, -9, -14, -8,
-12, -23, -24, -10, -20, -17, -78, -70, -50, -46,
-4, -5, -4, -8, 2, -1, -7, -6, -8, -34,
-3, 32, 30, -44, 0, -5, 0, -1, -3, -8,
-25, -14, -5, 5, 2, 0, -9, -11, 18, -4,
0, 7, 9, 18, 9, 5, 9, 0, 0, 2,
19, -4, 15, 12, 9, 0, 4, 10, 10, 33,
52, 18, 28, 35, 38, 34, 39, 32, 102, 0,
56, 33, 29, 37, 51, 39, 52, 69, 67, 44,
32, 55, 32, 0, 27, 33, 34, 36, 38, 38,
34, 35, 34, 32, 37, 35, 30, 28, 26, 29,
0, 2, 8, 14, 18, 17, 21, 17, 20, 18,
27, 16, 7, 16, 11, 10, -10, -23, -15, -7,
0, -5, -11, -9, -9, -10, -34, -21, -3, -5,
-7, -11, -15, -17, -25, -25, -28, -11, -10, -10,
-10, -9, -16, -7, -4, -5, -9, 2, -9, 1,
11, 5, -2, -2, 0, -8, 3, 7, 10, 17,
16, 3, -1, -5, -1, -4, 0, -21, -23, -20,
-26, -25, -17, -27, -27, -17, -26, -27, -33, -10,
-14, -8, -17, -28, -6, -2, -4, -9, -8, -1,
5, 1, 9, 0, 1, 7, -7, -6, -16, -2,
2, -6, -3, 2, -3, -3, 0, 9, -1, -2,
-2, -1, -9, 14, 16, 0, 18, 11, 12, 10,
2, 12, 13, 0, 3, 19, 3, 18, 19, 18,
14, 26, 31, 33, 33, 37, 39, 42, 47, 45,
49, 41, 32, 69, 63, 66, 77, 54, 52, 41,
36, 40, 30, 28, 23, 12, 11, 37, 39, 40,
38, 46, 42, 40, 49, 38, 40, 38, 46, 31,
29, 25, 12, 11, 26, 22, 23, 27, 33, 26,
30, 27, 18, 25, 18, 12, 21, 14, 11, 25,
21, 21, -5, -6, -10, -7, -17, -18, -4, -10,
-9, -9, -1, -8, -14, 0, 2, 17, 32, 42,
49, 53, 64, 68, 66, 47, -5, 0, -1, -2,
-2, -9, -6, -4, -4, -7, -3, -3, -7, -6,
-12, -14, -3, -6, -5, -5, 0, -4, -9, 1,
2, 17, 32, 42, 49, 53, 64, 68, 66, 47,
0, -5, -7, -4, 0, 0, -15, -35, -2, -12,
-9, -31, 0, -5, -7, -4, 0, 0, -15, -35,
-2, -12, -9, -31, -13, -13, -9, -14, -8, -12,
-23, -24, -10, -20, -17, -78, -70, -50, -46, -4,
-5, -4, -8, 2, -1, -7, -6, -8, -34, -3,
32, 30, -44, 0, -5, 0, -1, -3, -8, -25,
-14, -5, 5, 2, 0, -9, -11, 18, -13, -13,
-9, -14, -8, -12, -23, -24, -10, -20, -17, -78,
-70, -50, -46, -4, -5, -4, -8, 2, -1, -7,
-6, -8, -34, -3, 32, 30, -44, 0, -5, 0,
-1, -3, -8, -25, -14, -5, 5, 2, 0, -9,
-11, 18, 4, 10, 10, 33, 52, 18, 28, 35,
38, 34, 39, 32, 102, 0, 56, 33, 29, 37,
51, 39, 52, 69, 67, 44, 32, 55, 32, 0,
27, 33, 34, 36, 38, 38, 34, 35, 34, 32,
37, 35, 30, 28, 26, 29, 4, 10, 10, 33,
52, 18, 28, 35, 38, 34, 39, 32, 102, 0,
56, 33, 29, 37, 51, 39, 52, 69, 67, 44,
32, 55, 32, 0, 27, 33, 34, 36, 38, 38,
34, 35, 34, 32, 37, 35, 30, 28, 26, 29,
-5, -6, -10, -7, -17, -18, -4, -10, -9, -9,
-1, -8, -14, 0, 2, -3, -3, -7, -6, -12,
-14, -3, -6, -5, -5, 0, -4, -9, 1, 2,
17, 32, 42, 49, 53, 64, 68, 66, 47, 17,
32, 42, 49, 53, 64, 68, 66, 47, -5, 0,
-1, -2, -2, -9, -6, -4, -4, -7, -5, -6,
-10, -7, -17, -18, -4, -10, -9, -9, -1, -8,
-14, 0, 2, -3, -3, -7, -6, -12, -14, -3,
-6, -5, -5, 0, -4, -9, 1, 2, 17, 32,
42, 49, 53, 64, 68, 66, 47, 17, 32, 42,
49, 53, 64, 68, 66, 47, -5, 0, -1, -2,
-2, -9, -6, -4, -4, -7, -21, -23, -20, -26,
-25, -17, -27, -27, -17, -26, -27, -33, -10, -14,
-8, -17, -28, -6, -2, -4, -9, -8, -1, 5,
1, 9, 0, 1, 7, -7, -6, -16, -2, 2,
-6, -3, 2, -3, -3, 0, 9, -1, -2, -2,
-21, -23, -20, -26, -25, -17, -27, -27, -17, -26,
-27, -33, -10, -14, -8, -17, -28, -6, -2, -4,
-9, -8, -1, 5, 1, 9, 0, 1, 7, -7,
-6, -16, -2, 2, -6, -3, 2, -3, -3, 0,
9, -1, -2, -2, 19, 18, 14, 26, 31, 33,
33, 37, 39, 42, 47, 45, 49, 41, 32, 69,
63, 66, 77, 54, 52, 41, 36, 40, 30, 28,
23, 12, 11, 37, 39, 40, 38, 46, 42, 40,
49, 38, 40, 38, 46, 31, 29, 25, 19, 18,
14, 26, 31, 33, 33, 37, 39, 42, 47, 45,
49, 41, 32, 69, 63, 66, 77, 54, 52, 41,
36, 40, 30, 28, 23, 12, 11, 37, 39, 40,
38, 46, 42, 40, 49, 38, 40, 38, 46, 31,
29, 25, -23, -15, -7, 0, -5, -11, -9, -9,
-10, -34, -21, -3, -5, -7, -11, -15, -17, -25,
-25, -28, -11, -10, -10, -10, -9, -16, -7, -4,
-5, -9, -23, -15, -7, 0, -5, -11, -9, -9,
-10, -34, -21, -3, -5, -7, -11, -15, -17, -25,
-25, -28, -11, -10, -10, -10, -9, -16, -7, -4,
-5, -9, -2, -12, -9, -31, -2, -12, -9, -31,
-2, -12, -9, -31 }, new int[] {
20, 2, 3, 20, 2, 3, -28, -23, -6, -1,
7, 29, 25, 14, -10, -3, -27, 26, -4, -24,
5, 6, -17, 14, 20, 20, 29, 54, 37, 12,
-32, -22, -2, -4, -24, 5, -6, -14, -6, 4,
-11, -15, -21, 19, 20, 4, 6, 1, -5, -13,
5, 6, -3, -1, 3, -4, -2, -12, -7, 1,
0, 0, 0, 0, -9, 4, 0, -7, 13, 3,
7, -9, -20, -36, -17, -14, -25, -25, -12, -17,
-31, -14, -18, -13, -37, 11, 5, 2, 5, -6,
4, -14, -37, -5, -11, -11, -30, 0, -2, 0,
-4, -6, 3, -8, -13, -4, -12, -5, -3, -4,
-8, -16, -9, -1, 5, 4, -4, -2, 2, -1,
-4, -1, 0, -7, -4, -6, -3, -6, 8, -9,
-11, 9, 0, -5, 1, -15, -5, -8, -21, -21,
-13, -25, -29, 9, 17, -8, -5, -2, 13, 3,
-7, 8, -10, 3, -3, -20, 0, 1, -3, -21,
16, -23, 17, 44, 50, -22, 4, 0, 7, 11,
8, 6, 7, 3, 8, 13, 13, 4, 3, 2,
6, 8, 11, 14, 7, 4, 4, 13, 9, 19,
10, 12, 0, 20, 8, 35, 33, 28, 24, 27,
34, 52, 39, 19, 31, 36, 24, 34, 30, 22,
20, 19, 27, 19, 15, 15, 21, 25, 30, 31,
27, 24, 0, 14, 15, 26, -24, -24, -22, -9,
0, 0, -14, -13, -13, -11, -29, -21, -14, -12,
-11, -10, -21, -16, -23, -15, -37, -10, -8, -8,
-8, -7, -14, -10, -9, -12, -18, -4, -22, -16,
-2, 1, -13, -9, -4, -13, -13, -6, -13, -6,
-2, -16, -10, -13, -9, -10, 0, -22, -25, -25,
-27, -19, -23, -25, -26, -24, -28, -31, -37, -10,
-15, -10, -13, -50, -5, 17, -5, -13, -12, -2,
0, -1, 4, -7, 5, 15, 1, 0, -10, 1,
0, 2, 0, -5, 7, 5, 2, 14, 15, 5,
2, -2, -18, 12, 5, -12, 11, 5, 0, 2,
-6, 5, 7, -6, -11, -2, -2, 25, 17, 16,
17, 27, 37, 41, 42, 48, 39, 46, 52, 46,
52, 43, 32, 61, 56, 62, 81, 45, 35, 28,
34, 39, 30, 20, 18, 15, 0, 36, 37, 37,
32, 34, 29, 24, 34, 31, 35, 31, 33, 36,
27, 21, 18, 19, 36, 24, 27, 24, 31, 22,
22, 16, 15, 14, 3, -16, 21, 22, 25, 21,
19, 17, -3, -8, -9, -10, -18, -12, -11, -5,
-17, -14, -16, -8, -14, -9, -11, 9, 30, 31,
33, 33, 31, 37, 31, 20, -9, -7, -8, -11,
-10, -12, -8, -9, -6, -10, -3, -8, -9, -10,
-18, -12, -11, -5, -17, -14, -16, -8, -14, -9,
-11, 9, 30, 31, 33, 33, 31, 37, 31, 20,
11, 5, 2, 5, -6, 4, -14, -37, -5, -11,
-11, -30, 11, 5, 2, 5, -6, 4, -14, -37,
-5, -11, -11, -30, -4, -12, -5, -3, -4, -8,
-16, -9, -1, 5, 4, -4, -2, 2, -1, -4,
-1, 0, -7, -4, -6, -3, -6, 8, -9, -11,
9, 0, -5, 1, -15, -5, -8, -21, -21, -13,
-25, -29, 9, 17, -8, -5, -2, 13, -4, -12,
-5, -3, -4, -8, -16, -9, -1, 5, 4, -4,
-2, 2, -1, -4, -1, 0, -7, -4, -6, -3,
-6, 8, -9, -11, 9, 0, -5, 1, -15, -5,
-8, -21, -21, -13, -25, -29, 9, 17, -8, -5,
-2, 13, 4, 0, 7, 11, 8, 6, 7, 3,
8, 13, 13, 4, 3, 2, 6, 8, 11, 14,
7, 4, 4, 13, 9, 19, 10, 12, 0, 20,
8, 35, 33, 28, 24, 27, 34, 52, 39, 19,
31, 36, 24, 34, 30, 22, 4, 0, 7, 11,
8, 6, 7, 3, 8, 13, 13, 4, 3, 2,
6, 8, 11, 14, 7, 4, 4, 13, 9, 19,
10, 12, 0, 20, 8, 35, 33, 28, 24, 27,
34, 52, 39, 19, 31, 36, 24, 34, 30, 22,
-3, -8, -9, -10, -18, -12, -11, -5, -17, -14,
-16, -8, -14, -9, -11, -3, -8, -9, -10, -18,
-12, -11, -5, -17, -14, -16, -8, -14, -9, -11,
9, 30, 31, 33, 33, 31, 37, 31, 20, 9,
30, 31, 33, 33, 31, 37, 31, 20, -9, -7,
-8, -11, -10, -12, -8, -9, -6, -10, -3, -8,
-9, -10, -18, -12, -11, -5, -17, -14, -16, -8,
-14, -9, -11, -3, -8, -9, -10, -18, -12, -11,
-5, -17, -14, -16, -8, -14, -9, -11, 9, 30,
31, 33, 33, 31, 37, 31, 20, 9, 30, 31,
33, 33, 31, 37, 31, 20, -9, -7, -8, -11,
-10, -12, -8, -9, -6, -10, -22, -25, -25, -27,
-19, -23, -25, -26, -24, -28, -31, -37, -10, -15,
-10, -13, -50, -5, 17, -5, -13, -12, -2, 0,
-1, 4, -7, 5, 15, 1, 0, -10, 1, 0,
2, 0, -5, 7, 5, 2, 14, 15, 5, 2,
-22, -25, -25, -27, -19, -23, -25, -26, -24, -28,
-31, -37, -10, -15, -10, -13, -50, -5, 17, -5,
-13, -12, -2, 0, -1, 4, -7, 5, 15, 1,
0, -10, 1, 0, 2, 0, -5, 7, 5, 2,
14, 15, 5, 2, 17, 16, 17, 27, 37, 41,
42, 48, 39, 46, 52, 46, 52, 43, 32, 61,
56, 62, 81, 45, 35, 28, 34, 39, 30, 20,
18, 15, 0, 36, 37, 37, 32, 34, 29, 24,
34, 31, 35, 31, 33, 36, 27, 21, 17, 16,
17, 27, 37, 41, 42, 48, 39, 46, 52, 46,
52, 43, 32, 61, 56, 62, 81, 45, 35, 28,
34, 39, 30, 20, 18, 15, 0, 36, 37, 37,
32, 34, 29, 24, 34, 31, 35, 31, 33, 36,
27, 21, -24, -22, -9, 0, 0, -14, -13, -13,
-11, -29, -21, -14, -12, -11, -10, -21, -16, -23,
-15, -37, -10, -8, -8, -8, -7, -14, -10, -9,
-12, -18, -24, -22, -9, 0, 0, -14, -13, -13,
-11, -29, -21, -14, -12, -11, -10, -21, -16, -23,
-15, -37, -10, -8, -8, -8, -7, -14, -10, -9,
-12, -18, -5, -11, -11, -30, -5, -11, -11, -30,
-5, -11, -11, -30 } };
public static int[][] cabac_context_init_PB_B = new int[][] { new int[] {
-15, 54, 74, -15, 54, 74, 127, 104, 53, 54,
51, 33, 2, 0, 9, 49, 118, 57, 78, 65,
62, 49, 73, 50, 64, 43, 0, 67, 90, 104,
127, 104, 67, 78, 65, 62, 86, 95, 61, 45,
69, 81, 96, 55, 67, 86, 88, 58, 76, 94,
54, 69, 81, 88, 67, 74, 74, 80, 72, 58,
41, 63, 63, 63, 83, 86, 97, 72, 41, 62,
45, 78, 96, 126, 98, 101, 67, 82, 94, 83,
110, 91, 102, 93, 127, 92, 89, 96, 108, 46,
65, 57, 93, 74, 92, 87, 126, 54, 60, 59,
69, 48, 68, 69, 88, 85, 78, 75, 77, 54,
50, 68, 50, 42, 81, 63, 70, 67, 57, 76,
35, 64, 61, 35, 25, 24, 29, 36, 93, 73,
73, 46, 49, 100, 53, 53, 53, 61, 56, 56,
63, 60, 62, 57, 69, 57, 39, 51, 68, 64,
61, 63, 50, 39, 44, 52, 48, 60, 59, 59,
33, 44, 43, 78, 60, 69, 28, 40, 44, 49,
46, 44, 51, 47, 39, 62, 46, 54, 54, 58,
63, 51, 57, 53, 52, 55, 45, 36, 53, 82,
55, 78, 46, 31, 84, 7, -7, 3, 4, 0,
-1, 6, 6, 9, 19, 27, 30, 20, 34, 70,
67, 59, 67, 30, 32, 35, 29, 31, 38, 43,
41, 63, 59, 64, 94, 89, 108, 76, 44, 45,
52, 64, 59, 70, 75, 82, 102, 77, 24, 42,
48, 55, 59, 71, 83, 87, 119, 58, 29, 36,
38, 43, 55, 58, 64, 74, 90, 70, 29, 31,
42, 59, 58, 72, 81, 97, 58, 5, 14, 18,
27, 40, 58, 70, 79, 85, 0, 106, 106, 87,
114, 110, 98, 110, 106, 103, 107, 108, 112, 96,
95, 91, 93, 94, 86, 67, 80, 85, 70, 60,
58, 61, 50, 50, 49, 54, 41, 46, 51, 49,
52, 41, 47, 55, 41, 44, 50, 53, 49, 63,
64, 69, 59, 70, 44, 31, 43, 53, 34, 38,
52, 40, 32, 44, 38, 50, 57, 43, 11, 14,
11, 11, 9, -2, -15, -15, -21, -23, -33, -31,
-28, -12, 29, -24, -45, -26, -43, -19, -10, 9,
26, 27, 57, 82, 75, 97, 125, 0, 0, -4,
6, 8, 10, 22, 19, 32, 31, 41, 44, 47,
65, 71, 60, 63, 65, 24, 20, 23, 32, 23,
24, 40, 32, 29, 42, 57, 53, 61, 86, 40,
51, 59, 79, 71, 69, 70, 66, 68, 73, 69,
70, 67, 62, 70, 66, 65, 63, -2, -9, -9,
-7, -2, 3, 9, 27, 59, 66, 35, 42, 45,
48, 56, 60, 62, 66, 76, 85, 81, 77, 81,
80, 73, 74, 83, 71, 67, 61, 66, 66, 59,
59, -13, -14, -7, -2, 2, 6, 17, 34, 62,
92, 89, 96, 108, 46, 65, 57, 93, 74, 92,
87, 126, 92, 89, 96, 108, 46, 65, 57, 93,
74, 92, 87, 126, 85, 78, 75, 77, 54, 50,
68, 50, 42, 81, 63, 70, 67, 57, 76, 35,
64, 61, 35, 25, 24, 29, 36, 93, 73, 73,
46, 49, 100, 53, 53, 53, 61, 56, 56, 63,
60, 62, 57, 69, 57, 39, 51, 68, 85, 78,
75, 77, 54, 50, 68, 50, 42, 81, 63, 70,
67, 57, 76, 35, 64, 61, 35, 25, 24, 29,
36, 93, 73, 73, 46, 49, 100, 53, 53, 53,
61, 56, 56, 63, 60, 62, 57, 69, 57, 39,
51, 68, 28, 40, 44, 49, 46, 44, 51, 47,
39, 62, 46, 54, 54, 58, 63, 51, 57, 53,
52, 55, 45, 36, 53, 82, 55, 78, 46, 31,
84, 7, -7, 3, 4, 0, -1, 6, 6, 9,
19, 27, 30, 20, 34, 70, 28, 40, 44, 49,
46, 44, 51, 47, 39, 62, 46, 54, 54, 58,
63, 51, 57, 53, 52, 55, 45, 36, 53, 82,
55, 78, 46, 31, 84, 7, -7, 3, 4, 0,
-1, 6, 6, 9, 19, 27, 30, 20, 34, 70,
79, 71, 69, 70, 66, 68, 73, 69, 70, 67,
62, 70, 66, 65, 63, 85, 81, 77, 81, 80,
73, 74, 83, 71, 67, 61, 66, 66, 59, 59,
-2, -9, -9, -7, -2, 3, 9, 27, 59, -13,
-14, -7, -2, 2, 6, 17, 34, 62, 66, 35,
42, 45, 48, 56, 60, 62, 66, 76, 79, 71,
69, 70, 66, 68, 73, 69, 70, 67, 62, 70,
66, 65, 63, 85, 81, 77, 81, 80, 73, 74,
83, 71, 67, 61, 66, 66, 59, 59, -2, -9,
-9, -7, -2, 3, 9, 27, 59, -13, -14, -7,
-2, 2, 6, 17, 34, 62, 66, 35, 42, 45,
48, 56, 60, 62, 66, 76, 106, 106, 87, 114,
110, 98, 110, 106, 103, 107, 108, 112, 96, 95,
91, 93, 94, 86, 67, 80, 85, 70, 60, 58,
61, 50, 50, 49, 54, 41, 46, 51, 49, 52,
41, 47, 55, 41, 44, 50, 53, 49, 63, 64,
106, 106, 87, 114, 110, 98, 110, 106, 103, 107,
108, 112, 96, 95, 91, 93, 94, 86, 67, 80,
85, 70, 60, 58, 61, 50, 50, 49, 54, 41,
46, 51, 49, 52, 41, 47, 55, 41, 44, 50,
53, 49, 63, 64, 11, 14, 11, 11, 9, -2,
-15, -15, -21, -23, -33, -31, -28, -12, 29, -24,
-45, -26, -43, -19, -10, 9, 26, 27, 57, 82,
75, 97, 125, 0, 0, -4, 6, 8, 10, 22,
19, 32, 31, 41, 44, 47, 65, 71, 11, 14,
11, 11, 9, -2, -15, -15, -21, -23, -33, -31,
-28, -12, 29, -24, -45, -26, -43, -19, -10, 9,
26, 27, 57, 82, 75, 97, 125, 0, 0, -4,
6, 8, 10, 22, 19, 32, 31, 41, 44, 47,
65, 71, 76, 44, 45, 52, 64, 59, 70, 75,
82, 102, 77, 24, 42, 48, 55, 59, 71, 83,
87, 119, 58, 29, 36, 38, 43, 55, 58, 64,
74, 90, 76, 44, 45, 52, 64, 59, 70, 75,
82, 102, 77, 24, 42, 48, 55, 59, 71, 83,
87, 119, 58, 29, 36, 38, 43, 55, 58, 64,
74, 90, 74, 92, 87, 126, 74, 92, 87, 126,
74, 92, 87, 126 }, new int[] {
-15, 54, 74, -15, 54, 74, 127, 104, 53, 54,
51, 25, 0, 0, 9, 41, 118, 65, 71, 79,
52, 50, 70, 54, 34, 22, 0, 2, 36, 69,
127, 101, 76, 71, 79, 52, 69, 90, 52, 43,
69, 82, 96, 59, 75, 87, 100, 56, 74, 85,
59, 81, 86, 95, 66, 77, 70, 86, 72, 61,
41, 63, 63, 63, 83, 86, 97, 72, 41, 62,
15, 51, 80, 127, 91, 96, 81, 98, 102, 97,
119, 99, 110, 102, 127, 80, 89, 94, 92, 39,
65, 84, 127, 73, 104, 91, 127, 55, 56, 55,
61, 53, 68, 74, 88, 103, 91, 89, 92, 76,
87, 110, 105, 78, 112, 99, 127, 127, 127, 127,
66, 78, 71, 72, 59, 55, 70, 75, 89, 119,
75, 20, 22, 127, 54, 61, 58, 60, 61, 67,
84, 74, 65, 52, 57, 61, 69, 70, 55, 71,
58, 61, 41, 25, 32, 43, 47, 44, 51, 46,
38, 66, 38, 42, 34, 89, 45, 28, 31, -11,
-43, 15, 0, -22, -25, 0, -18, -12, -94, 0,
-15, -4, 10, -5, -29, -9, -34, -58, -63, -5,
7, -29, 1, 0, 36, -25, -30, -28, -28, -27,
-18, -16, -14, -8, -6, 0, 10, 18, 25, 41,
75, 72, 77, 35, 31, 35, 30, 45, 42, 45,
26, 54, 66, 56, 73, 67, 116, 112, 71, 61,
53, 66, 77, 80, 84, 87, 127, 101, 39, 53,
61, 75, 77, 91, 107, 111, 122, 76, 44, 52,
57, 58, 72, 69, 69, 74, 86, 66, 34, 32,
31, 52, 55, 67, 73, 89, 52, 4, 8, 8,
19, 37, 61, 73, 70, 78, 0, 126, 124, 110,
126, 124, 105, 121, 117, 102, 117, 116, 122, 95,
100, 95, 111, 114, 89, 80, 82, 85, 81, 72,
64, 67, 56, 69, 69, 69, 69, 67, 77, 64,
61, 67, 64, 57, 65, 66, 62, 51, 66, 71,
75, 70, 72, 60, 37, 47, 35, 37, 41, 41,
48, 41, 41, 59, 50, 40, 66, 50, -6, -6,
0, -12, -16, -25, -22, -28, -30, -30, -42, -36,
-34, -17, 9, -71, -63, -64, -74, -39, -35, -10,
0, -1, 14, 26, 37, 55, 65, -33, -36, -37,
-30, -33, -30, -24, -29, -12, -10, -3, -5, 20,
30, 44, 48, 49, 45, 22, 22, 21, 20, 28,
24, 34, 42, 39, 50, 70, 54, 71, 83, 32,
49, 54, 85, 81, 77, 81, 80, 73, 74, 83,
71, 67, 61, 66, 66, 59, 59, -10, -13, -9,
-5, 0, 3, 10, 27, 57, 71, 24, 36, 42,
52, 57, 63, 65, 67, 82, 81, 76, 72, 78,
72, 68, 70, 76, 66, 62, 57, 61, 60, 54,
58, -10, -13, -9, -5, 0, 3, 10, 27, 57,
80, 89, 94, 92, 39, 65, 84, 127, 73, 104,
91, 127, 80, 89, 94, 92, 39, 65, 84, 127,
73, 104, 91, 127, 103, 91, 89, 92, 76, 87,
110, 105, 78, 112, 99, 127, 127, 127, 127, 66,
78, 71, 72, 59, 55, 70, 75, 89, 119, 75,
20, 22, 127, 54, 61, 58, 60, 61, 67, 84,
74, 65, 52, 57, 61, 69, 70, 55, 103, 91,
89, 92, 76, 87, 110, 105, 78, 112, 99, 127,
127, 127, 127, 66, 78, 71, 72, 59, 55, 70,
75, 89, 119, 75, 20, 22, 127, 54, 61, 58,
60, 61, 67, 84, 74, 65, 52, 57, 61, 69,
70, 55, 45, 28, 31, -11, -43, 15, 0, -22,
-25, 0, -18, -12, -94, 0, -15, -4, 10, -5,
-29, -9, -34, -58, -63, -5, 7, -29, 1, 0,
36, -25, -30, -28, -28, -27, -18, -16, -14, -8,
-6, 0, 10, 18, 25, 41, 45, 28, 31, -11,
-43, 15, 0, -22, -25, 0, -18, -12, -94, 0,
-15, -4, 10, -5, -29, -9, -34, -58, -63, -5,
7, -29, 1, 0, 36, -25, -30, -28, -28, -27,
-18, -16, -14, -8, -6, 0, 10, 18, 25, 41,
85, 81, 77, 81, 80, 73, 74, 83, 71, 67,
61, 66, 66, 59, 59, 81, 76, 72, 78, 72,
68, 70, 76, 66, 62, 57, 61, 60, 54, 58,
-10, -13, -9, -5, 0, 3, 10, 27, 57, -10,
-13, -9, -5, 0, 3, 10, 27, 57, 71, 24,
36, 42, 52, 57, 63, 65, 67, 82, 85, 81,
77, 81, 80, 73, 74, 83, 71, 67, 61, 66,
66, 59, 59, 81, 76, 72, 78, 72, 68, 70,
76, 66, 62, 57, 61, 60, 54, 58, -10, -13,
-9, -5, 0, 3, 10, 27, 57, -10, -13, -9,
-5, 0, 3, 10, 27, 57, 71, 24, 36, 42,
52, 57, 63, 65, 67, 82, 126, 124, 110, 126,
124, 105, 121, 117, 102, 117, 116, 122, 95, 100,
95, 111, 114, 89, 80, 82, 85, 81, 72, 64,
67, 56, 69, 69, 69, 69, 67, 77, 64, 61,
67, 64, 57, 65, 66, 62, 51, 66, 71, 75,
126, 124, 110, 126, 124, 105, 121, 117, 102, 117,
116, 122, 95, 100, 95, 111, 114, 89, 80, 82,
85, 81, 72, 64, 67, 56, 69, 69, 69, 69,
67, 77, 64, 61, 67, 64, 57, 65, 66, 62,
51, 66, 71, 75, -6, -6, 0, -12, -16, -25,
-22, -28, -30, -30, -42, -36, -34, -17, 9, -71,
-63, -64, -74, -39, -35, -10, 0, -1, 14, 26,
37, 55, 65, -33, -36, -37, -30, -33, -30, -24,
-29, -12, -10, -3, -5, 20, 30, 44, -6, -6,
0, -12, -16, -25, -22, -28, -30, -30, -42, -36,
-34, -17, 9, -71, -63, -64, -74, -39, -35, -10,
0, -1, 14, 26, 37, 55, 65, -33, -36, -37,
-30, -33, -30, -24, -29, -12, -10, -3, -5, 20,
30, 44, 112, 71, 61, 53, 66, 77, 80, 84,
87, 127, 101, 39, 53, 61, 75, 77, 91, 107,
111, 122, 76, 44, 52, 57, 58, 72, 69, 69,
74, 86, 112, 71, 61, 53, 66, 77, 80, 84,
87, 127, 101, 39, 53, 61, 75, 77, 91, 107,
111, 122, 76, 44, 52, 57, 58, 72, 69, 69,
74, 86, 73, 104, 91, 127, 73, 104, 91, 127,
73, 104, 91, 127 }, new int[] {
-15, 54, 74, -15, 54, 74, 127, 104, 53, 54,
51, 16, 0, 0, 51, 62, 99, 16, 85, 102,
57, 57, 73, 57, 40, 10, 0, 0, 42, 97,
127, 117, 74, 85, 102, 57, 93, 88, 44, 55,
89, 103, 116, 57, 58, 84, 96, 63, 85, 106,
63, 75, 90, 101, 55, 79, 75, 97, 50, 60,
41, 63, 63, 63, 83, 86, 97, 72, 41, 62,
34, 88, 127, 127, 91, 95, 84, 86, 89, 91,
127, 76, 103, 90, 127, 80, 76, 84, 78, 55,
61, 83, 127, 79, 104, 91, 127, 65, 79, 72,
92, 56, 68, 71, 98, 86, 88, 82, 72, 67,
72, 89, 69, 59, 66, 57, 71, 71, 58, 74,
44, 69, 62, 51, 47, 42, 41, 53, 76, 78,
83, 52, 67, 90, 67, 72, 75, 80, 83, 64,
31, 64, 94, 75, 63, 74, 35, 27, 91, 65,
69, 77, 66, 62, 68, 81, 30, 7, 23, 74,
66, 124, 37, -18, -34, 127, 39, 42, 34, 29,
31, 37, 42, 40, 33, 43, 36, 47, 55, 58,
60, 44, 44, 42, 48, 56, 52, 37, 49, 58,
48, 45, 69, 33, 63, -18, -25, -3, 10, 0,
-14, -44, -24, 17, 25, 29, 33, 15, 20, 73,
34, 31, 44, 16, 36, 36, 28, 21, 20, 12,
16, 42, 93, 56, 57, 38, 127, 115, 82, 62,
53, 59, 85, 89, 94, 92, 127, 100, 57, 67,
71, 77, 85, 88, 104, 98, 127, 82, 48, 61,
66, 70, 75, 79, 83, 92, 108, 79, 69, 75,
58, 58, 78, 83, 81, 99, 81, 38, 62, 58,
59, 73, 76, 86, 83, 87, 0, 127, 127, 120,
127, 114, 117, 118, 117, 113, 118, 120, 124, 94,
102, 99, 106, 127, 92, 57, 86, 94, 91, 77,
71, 73, 64, 81, 64, 57, 67, 68, 67, 68,
77, 64, 68, 78, 55, 59, 65, 54, 44, 60,
70, 76, 86, 70, 64, 70, 55, 56, 69, 65,
74, 54, 54, 76, 82, 77, 77, 42, -13, -9,
-12, -21, -30, -40, -41, -47, -32, -40, -51, -41,
-39, -19, 11, -55, -46, -50, -67, -20, -2, 15,
1, 1, 17, 38, 45, 54, 79, -16, -14, -17,
1, 15, 15, 25, 22, 16, 18, 28, 41, 28,
47, 62, 31, 26, 24, 23, 16, 30, 29, 41,
42, 60, 52, 60, 78, 123, 53, 56, 61, 33,
50, 61, 78, 74, 72, 72, 75, 71, 63, 70,
75, 72, 67, 53, 59, 52, 68, -2, -10, -4,
-1, 7, 12, 23, 38, 64, 71, 37, 44, 49,
56, 59, 63, 67, 68, 79, 78, 74, 72, 72,
75, 71, 63, 70, 75, 72, 67, 53, 59, 52,
68, -2, -10, -4, -1, 7, 12, 23, 38, 64,
80, 76, 84, 78, 55, 61, 83, 127, 79, 104,
91, 127, 80, 76, 84, 78, 55, 61, 83, 127,
79, 104, 91, 127, 86, 88, 82, 72, 67, 72,
89, 69, 59, 66, 57, 71, 71, 58, 74, 44,
69, 62, 51, 47, 42, 41, 53, 76, 78, 83,
52, 67, 90, 67, 72, 75, 80, 83, 64, 31,
64, 94, 75, 63, 74, 35, 27, 91, 86, 88,
82, 72, 67, 72, 89, 69, 59, 66, 57, 71,
71, 58, 74, 44, 69, 62, 51, 47, 42, 41,
53, 76, 78, 83, 52, 67, 90, 67, 72, 75,
80, 83, 64, 31, 64, 94, 75, 63, 74, 35,
27, 91, 39, 42, 34, 29, 31, 37, 42, 40,
33, 43, 36, 47, 55, 58, 60, 44, 44, 42,
48, 56, 52, 37, 49, 58, 48, 45, 69, 33,
63, -18, -25, -3, 10, 0, -14, -44, -24, 17,
25, 29, 33, 15, 20, 73, 39, 42, 34, 29,
31, 37, 42, 40, 33, 43, 36, 47, 55, 58,
60, 44, 44, 42, 48, 56, 52, 37, 49, 58,
48, 45, 69, 33, 63, -18, -25, -3, 10, 0,
-14, -44, -24, 17, 25, 29, 33, 15, 20, 73,
78, 74, 72, 72, 75, 71, 63, 70, 75, 72,
67, 53, 59, 52, 68, 78, 74, 72, 72, 75,
71, 63, 70, 75, 72, 67, 53, 59, 52, 68,
-2, -10, -4, -1, 7, 12, 23, 38, 64, -2,
-10, -4, -1, 7, 12, 23, 38, 64, 71, 37,
44, 49, 56, 59, 63, 67, 68, 79, 78, 74,
72, 72, 75, 71, 63, 70, 75, 72, 67, 53,
59, 52, 68, 78, 74, 72, 72, 75, 71, 63,
70, 75, 72, 67, 53, 59, 52, 68, -2, -10,
-4, -1, 7, 12, 23, 38, 64, -2, -10, -4,
-1, 7, 12, 23, 38, 64, 71, 37, 44, 49,
56, 59, 63, 67, 68, 79, 127, 127, 120, 127,
114, 117, 118, 117, 113, 118, 120, 124, 94, 102,
99, 106, 127, 92, 57, 86, 94, 91, 77, 71,
73, 64, 81, 64, 57, 67, 68, 67, 68, 77,
64, 68, 78, 55, 59, 65, 54, 44, 60, 70,
127, 127, 120, 127, 114, 117, 118, 117, 113, 118,
120, 124, 94, 102, 99, 106, 127, 92, 57, 86,
94, 91, 77, 71, 73, 64, 81, 64, 57, 67,
68, 67, 68, 77, 64, 68, 78, 55, 59, 65,
54, 44, 60, 70, -13, -9, -12, -21, -30, -40,
-41, -47, -32, -40, -51, -41, -39, -19, 11, -55,
-46, -50, -67, -20, -2, 15, 1, 1, 17, 38,
45, 54, 79, -16, -14, -17, 1, 15, 15, 25,
22, 16, 18, 28, 41, 28, 47, 62, -13, -9,
-12, -21, -30, -40, -41, -47, -32, -40, -51, -41,
-39, -19, 11, -55, -46, -50, -67, -20, -2, 15,
1, 1, 17, 38, 45, 54, 79, -16, -14, -17,
1, 15, 15, 25, 22, 16, 18, 28, 41, 28,
47, 62, 115, 82, 62, 53, 59, 85, 89, 94,
92, 127, 100, 57, 67, 71, 77, 85, 88, 104,
98, 127, 82, 48, 61, 66, 70, 75, 79, 83,
92, 108, 115, 82, 62, 53, 59, 85, 89, 94,
92, 127, 100, 57, 67, 71, 77, 85, 88, 104,
98, 127, 82, 48, 61, 66, 70, 75, 79, 83,
92, 108, 79, 104, 91, 127, 79, 104, 91, 127,
79, 104, 91, 127 } };
}

View file

@ -0,0 +1,71 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Utils2;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.tools.Debug;
public class CAVLCReader {
public static int readNBit(BitReader bits, int n, String message) {
int val = bits.readNBit(n);
Debug.trace(message, val);
return val;
}
public static int readUE(BitReader bits) {
int cnt = 0;
while (bits.read1Bit() == 0 && cnt < 32)
cnt++;
int res = 0;
if (cnt > 0) {
long val = (long)bits.readNBit(cnt);
res = (int)((long)((1 << cnt) - 1) + val);
}
return res;
}
public static int readUEtrace(BitReader bits, String message) {
int res = readUE(bits);
Debug.trace(message, res);
return res;
}
public static int readSE(BitReader bits, String message) {
int val = readUE(bits);
val = H264Utils2.golomb2Signed(val);
Debug.trace(message, val);
return val;
}
public static boolean readBool(BitReader bits, String message) {
boolean res = !(bits.read1Bit() == 0);
Debug.trace(message, res ? 1 : 0);
return res;
}
public static int readU(BitReader bits, int i, String string) {
return readNBit(bits, i, string);
}
public static int readTE(BitReader bits, int max) {
if (max > 1)
return readUE(bits);
return (bits.read1Bit() ^ 0xFFFFFFFF) & 0x1;
}
public static int readME(BitReader bits, String string) {
return readUEtrace(bits, string);
}
public static int readZeroBitCount(BitReader bits, String message) {
int count = 0;
while (bits.read1Bit() == 0 && count < 32)
count++;
if (Debug.debug)
Debug.trace(message, String.valueOf(count));
return count;
}
public static boolean moreRBSPData(BitReader bits) {
return (bits.remaining() >= 32 || bits.checkNBit(1) != 1 || bits.checkNBit(24) << 9 != 0);
}
}

View file

@ -0,0 +1,143 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.common.tools.MathUtil;
public class ChromaPredictionBuilder {
public static void predictWithMode(int[][] residual, int chromaMode, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, byte[] pixOut) {
switch (chromaMode) {
case 0:
predictDC(residual, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
break;
case 1:
predictHorizontal(residual, mbX, leftAvailable, leftRow, pixOut);
break;
case 2:
predictVertical(residual, mbX, topAvailable, topLine, pixOut);
break;
case 3:
predictPlane(residual, mbX, leftAvailable, topAvailable, leftRow, topLine, topLeft, pixOut);
break;
}
}
public static void predictDC(int[][] planeData, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] pixOut) {
predictDCInside(planeData, 0, 0, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
predictDCTopBorder(planeData, 1, 0, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
predictDCLeftBorder(planeData, 0, 1, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
predictDCInside(planeData, 1, 1, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
}
public static void predictVertical(int[][] residual, int mbX, boolean topAvailable, byte[] topLine, byte[] pixOut) {
for (int off = 0, j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++, off++)
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + topLine[(mbX << 3) + i], -128, 127);
}
}
public static void predictHorizontal(int[][] residual, int mbX, boolean leftAvailable, byte[] leftRow, byte[] pixOut) {
for (int off = 0, j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++, off++)
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + leftRow[j], -128, 127);
}
}
public static void predictDCInside(int[][] residual, int blkX, int blkY, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] pixOut) {
int s0;
int blkOffX = (blkX << 2) + (mbX << 3), blkOffY = blkY << 2;
if (leftAvailable && topAvailable) {
s0 = 0;
for (int k = 0; k < 4; k++)
s0 += leftRow[k + blkOffY];
for (int i = 0; i < 4; i++)
s0 += topLine[blkOffX + i];
s0 = s0 + 4 >> 3;
} else if (leftAvailable) {
s0 = 0;
for (int i = 0; i < 4; i++)
s0 += leftRow[blkOffY + i];
s0 = s0 + 2 >> 2;
} else if (topAvailable) {
s0 = 0;
for (int i = 0; i < 4; i++)
s0 += topLine[blkOffX + i];
s0 = s0 + 2 >> 2;
} else {
s0 = 0;
}
for (int off = (blkY << 5) + (blkX << 2), j = 0; j < 4; j++, off += 8) {
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + s0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 1]][H264Const.CHROMA_POS_LUT[off + 1]] + s0, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 2]][H264Const.CHROMA_POS_LUT[off + 2]] + s0, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 3]][H264Const.CHROMA_POS_LUT[off + 3]] + s0, -128, 127);
}
}
public static void predictDCTopBorder(int[][] residual, int blkX, int blkY, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] pixOut) {
int s1;
int blkOffX = (blkX << 2) + (mbX << 3), blkOffY = blkY << 2;
if (topAvailable) {
s1 = 0;
for (int i = 0; i < 4; i++)
s1 += topLine[blkOffX + i];
s1 = s1 + 2 >> 2;
} else if (leftAvailable) {
s1 = 0;
for (int i = 0; i < 4; i++)
s1 += leftRow[blkOffY + i];
s1 = s1 + 2 >> 2;
} else {
s1 = 0;
}
for (int off = (blkY << 5) + (blkX << 2), j = 0; j < 4; j++, off += 8) {
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + s1, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 1]][H264Const.CHROMA_POS_LUT[off + 1]] + s1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 2]][H264Const.CHROMA_POS_LUT[off + 2]] + s1, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 3]][H264Const.CHROMA_POS_LUT[off + 3]] + s1, -128, 127);
}
}
public static void predictDCLeftBorder(int[][] residual, int blkX, int blkY, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] pixOut) {
int s2;
int blkOffX = (blkX << 2) + (mbX << 3), blkOffY = blkY << 2;
if (leftAvailable) {
s2 = 0;
for (int i = 0; i < 4; i++)
s2 += leftRow[blkOffY + i];
s2 = s2 + 2 >> 2;
} else if (topAvailable) {
s2 = 0;
for (int i = 0; i < 4; i++)
s2 += topLine[blkOffX + i];
s2 = s2 + 2 >> 2;
} else {
s2 = 0;
}
for (int off = (blkY << 5) + (blkX << 2), j = 0; j < 4; j++, off += 8) {
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + s2, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 1]][H264Const.CHROMA_POS_LUT[off + 1]] + s2, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 2]][H264Const.CHROMA_POS_LUT[off + 2]] + s2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 3]][H264Const.CHROMA_POS_LUT[off + 3]] + s2, -128, 127);
}
}
public static void predictPlane(int[][] residual, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, byte[] pixOut) {
int H = 0, blkOffX = mbX << 3;
for (int i = 0; i < 3; i++)
H += (i + 1) * (topLine[blkOffX + 4 + i] - topLine[blkOffX + 2 - i]);
H += 4 * (topLine[blkOffX + 7] - topLeft[0]);
int V = 0;
for (int j = 0; j < 3; j++)
V += (j + 1) * (leftRow[4 + j] - leftRow[2 - j]);
V += 4 * (leftRow[7] - topLeft[0]);
int c = 34 * V + 32 >> 6;
int b = 34 * H + 32 >> 6;
int a = 16 * (leftRow[7] + topLine[blkOffX + 7]);
for (int off = 0, k = 0; k < 8; k++) {
for (int m = 0; m < 8; m++, off++) {
int val = a + b * (m - 3) + c * (k - 3) + 16 >> 5;
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + MathUtil.clip(val, -128, 127), -128, 127);
}
}
}
}

View file

@ -0,0 +1,421 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.common.ArrayUtil;
public class CoeffTransformer {
public static int[] zigzag4x4 = new int[] {
0, 1, 4, 8, 5, 2, 3, 6, 9, 12,
13, 10, 7, 11, 14, 15 };
public static int[] invZigzag4x4 = new int[16];
static int[][] dequantCoef = new int[][] { new int[] {
10, 13, 10, 13, 13, 16, 13, 16, 10, 13,
10, 13, 13, 16, 13, 16 }, new int[] {
11, 14, 11, 14, 14, 18, 14, 18, 11, 14,
11, 14, 14, 18, 14, 18 }, new int[] {
13, 16, 13, 16, 16, 20, 16, 20, 13, 16,
13, 16, 16, 20, 16, 20 }, new int[] {
14, 18, 14, 18, 18, 23, 18, 23, 14, 18,
14, 18, 18, 23, 18, 23 }, new int[] {
16, 20, 16, 20, 20, 25, 20, 25, 16, 20,
16, 20, 20, 25, 20, 25 }, new int[] {
18, 23, 18, 23, 23, 29, 23, 29, 18, 23,
18, 23, 23, 29, 23, 29 } };
static int[][] dequantCoef8x8 = new int[6][64];
static int[][] initDequantCoeff8x8 = new int[][] { new int[] { 20, 18, 32, 19, 25, 24 }, new int[] { 22, 19, 35, 21, 28, 26 }, new int[] { 26, 23, 42, 24, 33, 31 }, new int[] { 28, 25, 45, 26, 35, 33 }, new int[] { 32, 28, 51, 30, 40, 38 }, new int[] { 36, 32, 58, 34, 46, 43 } };
public static int[] zigzag8x8 = new int[] {
0, 1, 8, 16, 9, 2, 3, 10, 17, 24,
32, 25, 18, 11, 4, 5, 12, 19, 26, 33,
40, 48, 41, 34, 27, 20, 13, 6, 7, 14,
21, 28, 35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
52, 45, 38, 31, 39, 46, 53, 60, 61, 54,
47, 55, 62, 63 };
public static int[] invZigzag8x8 = new int[64];
static {
for (int j = 0; j < 16; j++)
invZigzag4x4[zigzag4x4[j]] = j;
for (int i = 0; i < 64; i++)
invZigzag8x8[zigzag8x8[i]] = i;
}
private static final int[][] quantCoeff = new int[][] { new int[] {
13107, 8066, 13107, 8066, 8066, 5243, 8066, 5243, 13107, 8066,
13107, 8066, 8066, 5243, 8066, 5243 }, new int[] {
11916, 7490, 11916, 7490, 7490, 4660, 7490, 4660, 11916, 7490,
11916, 7490, 7490, 4660, 7490, 4660 }, new int[] {
10082, 6554, 10082, 6554, 6554, 4194, 6554, 4194, 10082, 6554,
10082, 6554, 6554, 4194, 6554, 4194 }, new int[] {
9362, 5825, 9362, 5825, 5825, 3647, 5825, 3647, 9362, 5825,
9362, 5825, 5825, 3647, 5825, 3647 }, new int[] {
8192, 5243, 8192, 5243, 5243, 3355, 5243, 3355, 8192, 5243,
8192, 5243, 5243, 3355, 5243, 3355 }, new int[] {
7282, 4559, 7282, 4559, 4559, 2893, 4559, 2893, 7282, 4559,
7282, 4559, 4559, 2893, 4559, 2893 } };
static {
for (int g = 0; g < 6; g++) {
Arrays.fill(dequantCoef8x8[g], initDequantCoeff8x8[g][5]);
for (int i4 = 0; i4 < 8; i4 += 4) {
for (int i5 = 0; i5 < 8; i5 += 4)
dequantCoef8x8[g][(i4 << 3) + i5] = initDequantCoeff8x8[g][0];
}
for (int i3 = 1; i3 < 8; i3 += 2) {
for (int i5 = 1; i5 < 8; i5 += 2)
dequantCoef8x8[g][(i3 << 3) + i5] = initDequantCoeff8x8[g][1];
}
for (int i2 = 2; i2 < 8; i2 += 4) {
for (int i5 = 2; i5 < 8; i5 += 4)
dequantCoef8x8[g][(i2 << 3) + i5] = initDequantCoeff8x8[g][2];
}
for (int i1 = 0; i1 < 8; i1 += 4) {
for (int i5 = 1; i5 < 8; i5 += 2)
dequantCoef8x8[g][(i1 << 3) + i5] = initDequantCoeff8x8[g][3];
}
for (int n = 1; n < 8; n += 2) {
for (int i5 = 0; i5 < 8; i5 += 4)
dequantCoef8x8[g][(n << 3) + i5] = initDequantCoeff8x8[g][3];
}
for (int m = 0; m < 8; m += 4) {
for (int i5 = 2; i5 < 8; i5 += 4)
dequantCoef8x8[g][(m << 3) + i5] = initDequantCoeff8x8[g][4];
}
for (int k = 2; k < 8; k += 4) {
for (int i5 = 0; i5 < 8; i5 += 4)
dequantCoef8x8[g][(k << 3) + i5] = initDequantCoeff8x8[g][4];
}
}
}
public static final void idct4x4(int[] block) {
_idct4x4(block, block);
}
public static final void _idct4x4(int[] block, int[] out) {
for (int k = 0; k < 16; k += 4) {
int e0 = block[k] + block[k + 2];
int e1 = block[k] - block[k + 2];
int e2 = (block[k + 1] >> 1) - block[k + 3];
int e3 = block[k + 1] + (block[k + 3] >> 1);
out[k] = e0 + e3;
out[k + 1] = e1 + e2;
out[k + 2] = e1 - e2;
out[k + 3] = e0 - e3;
}
for (int j = 0; j < 4; j++) {
int g0 = out[j] + out[j + 8];
int g1 = out[j] - out[j + 8];
int g2 = (out[j + 4] >> 1) - out[j + 12];
int g3 = out[j + 4] + (out[j + 12] >> 1);
out[j] = g0 + g3;
out[j + 4] = g1 + g2;
out[j + 8] = g1 - g2;
out[j + 12] = g0 - g3;
}
for (int i = 0; i < 16; i++)
out[i] = out[i] + 32 >> 6;
}
public static void fdct4x4(int[] block) {
for (int j = 0; j < 16; j += 4) {
int t0 = block[j] + block[j + 3];
int t1 = block[j + 1] + block[j + 2];
int t2 = block[j + 1] - block[j + 2];
int t3 = block[j] - block[j + 3];
block[j] = t0 + t1;
block[j + 1] = (t3 << 1) + t2;
block[j + 2] = t0 - t1;
block[j + 3] = t3 - (t2 << 1);
}
for (int i = 0; i < 4; i++) {
int t0 = block[i] + block[i + 12];
int t1 = block[i + 4] + block[i + 8];
int t2 = block[i + 4] - block[i + 8];
int t3 = block[i] - block[i + 12];
block[i] = t0 + t1;
block[i + 4] = t2 + (t3 << 1);
block[i + 8] = t0 - t1;
block[i + 12] = t3 - (t2 << 1);
}
}
public static void invDC4x4(int[] scaled) {
for (int j = 0; j < 16; j += 4) {
int e0 = scaled[j] + scaled[j + 2];
int e1 = scaled[j] - scaled[j + 2];
int e2 = scaled[j + 1] - scaled[j + 3];
int e3 = scaled[j + 1] + scaled[j + 3];
scaled[j] = e0 + e3;
scaled[j + 1] = e1 + e2;
scaled[j + 2] = e1 - e2;
scaled[j + 3] = e0 - e3;
}
for (int i = 0; i < 4; i++) {
int g0 = scaled[i] + scaled[i + 8];
int g1 = scaled[i] - scaled[i + 8];
int g2 = scaled[i + 4] - scaled[i + 12];
int g3 = scaled[i + 4] + scaled[i + 12];
scaled[i] = g0 + g3;
scaled[i + 4] = g1 + g2;
scaled[i + 8] = g1 - g2;
scaled[i + 12] = g0 - g3;
}
}
public static void fvdDC4x4(int[] scaled) {
for (int j = 0; j < 16; j += 4) {
int t0 = scaled[j] + scaled[j + 3];
int t1 = scaled[j + 1] + scaled[j + 2];
int t2 = scaled[j + 1] - scaled[j + 2];
int t3 = scaled[j] - scaled[j + 3];
scaled[j] = t0 + t1;
scaled[j + 1] = t3 + t2;
scaled[j + 2] = t0 - t1;
scaled[j + 3] = t3 - t2;
}
for (int i = 0; i < 4; i++) {
int t0 = scaled[i] + scaled[i + 12];
int t1 = scaled[i + 4] + scaled[i + 8];
int t2 = scaled[i + 4] - scaled[i + 8];
int t3 = scaled[i] - scaled[i + 12];
scaled[i] = t0 + t1 >> 1;
scaled[i + 4] = t2 + t3 >> 1;
scaled[i + 8] = t0 - t1 >> 1;
scaled[i + 12] = t3 - t2 >> 1;
}
}
public static void dequantizeAC(int[] coeffs, int qp, int[] scalingList) {
int group = qp % 6;
if (scalingList == null) {
int qbits = qp / 6;
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * dequantCoef[group][i] << qbits;
} else if (qp >= 24) {
int qbits = qp / 6 - 4;
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * dequantCoef[group][i] * scalingList[invZigzag4x4[i]] << qbits;
} else {
int qbits = 4 - qp / 6;
int addition = 1 << 3 - qp / 6;
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * scalingList[invZigzag4x4[i]] * dequantCoef[group][i] + addition >> qbits;
}
}
public static void quantizeAC(int[] coeffs, int qp) {
int level = qp / 6;
int offset = qp % 6;
int addition = 682 << qp / 6 + 4;
int qbits = 15 + level;
if (qp < 10) {
for (int i = 0; i < 16; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (Math.min(((coeffs[i] ^ sign) - sign) * quantCoeff[offset][i] + addition >> qbits, 2063) ^ sign) - sign;
}
} else {
for (int i = 0; i < 16; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (((coeffs[i] ^ sign) - sign) * quantCoeff[offset][i] + addition >> qbits ^ sign) - sign;
}
}
}
public static void dequantizeDC4x4(int[] coeffs, int qp, int[] scalingList) {
int group = qp % 6;
if (qp >= 36) {
int qbits = qp / 6 - 6;
if (scalingList == null) {
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * (dequantCoef[group][0] << 4) << qbits;
} else {
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * dequantCoef[group][0] * scalingList[0] << qbits;
}
} else {
int qbits = 6 - qp / 6;
int addition = 1 << 5 - qp / 6;
if (scalingList == null) {
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * (dequantCoef[group][0] << 4) + addition >> qbits;
} else {
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * dequantCoef[group][0] * scalingList[0] + addition >> qbits;
}
}
}
public static void quantizeDC4x4(int[] coeffs, int qp) {
int level = qp / 6;
int offset = qp % 6;
int addition = 682 << qp / 6 + 5;
int qbits = 16 + level;
if (qp < 10) {
for (int i = 0; i < 16; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (Math.min(((coeffs[i] ^ sign) - sign) * quantCoeff[offset][0] + addition >> qbits, 2063) ^ sign) - sign;
}
} else {
for (int i = 0; i < 16; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (((coeffs[i] ^ sign) - sign) * quantCoeff[offset][0] + addition >> qbits ^ sign) - sign;
}
}
}
public static void invDC2x2(int[] block) {
int t0 = block[0] + block[1];
int t1 = block[0] - block[1];
int t2 = block[2] + block[3];
int t3 = block[2] - block[3];
block[0] = t0 + t2;
block[1] = t1 + t3;
block[2] = t0 - t2;
block[3] = t1 - t3;
}
public static void fvdDC2x2(int[] block) {
invDC2x2(block);
}
public static void dequantizeDC2x2(int[] transformed, int qp, int[] scalingList) {
int group = qp % 6;
if (scalingList == null) {
int shift = qp / 6;
for (int i = 0; i < 4; i++)
transformed[i] = transformed[i] * dequantCoef[group][0] << shift >> 1;
} else if (qp >= 24) {
int qbits = qp / 6 - 4;
for (int i = 0; i < 4; i++)
transformed[i] = transformed[i] * dequantCoef[group][0] * scalingList[0] << qbits >> 1;
} else {
int qbits = 4 - qp / 6;
int addition = 1 << 3 - qp / 6;
for (int i = 0; i < 4; i++)
transformed[i] = transformed[i] * dequantCoef[group][0] * scalingList[0] + addition >> qbits >> 1;
}
}
public static void quantizeDC2x2(int[] coeffs, int qp) {
int level = qp / 6;
int offset = qp % 6;
int addition = 682 << qp / 6 + 5;
int qbits = 16 + level;
if (qp < 4) {
for (int i = 0; i < 4; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (Math.min(((coeffs[i] ^ sign) - sign) * quantCoeff[offset][0] + addition >> qbits, 2063) ^ sign) - sign;
}
} else {
for (int i = 0; i < 4; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (((coeffs[i] ^ sign) - sign) * quantCoeff[offset][0] + addition >> qbits ^ sign) - sign;
}
}
}
public static void reorderDC4x4(int[] dc) {
ArrayUtil.swap(dc, 2, 4);
ArrayUtil.swap(dc, 3, 5);
ArrayUtil.swap(dc, 10, 12);
ArrayUtil.swap(dc, 11, 13);
}
public static void fvdDC4x2(int[] dc) {}
public static void quantizeDC4x2(int[] dc, int qp) {}
public static void invDC4x2(int[] dc) {}
public static void dequantizeDC4x2(int[] dc, int qp) {}
public static void dequantizeAC8x8(int[] coeffs, int qp, int[] scalingList) {
int group = qp % 6;
if (qp >= 36) {
int qbits = qp / 6 - 6;
if (scalingList == null) {
for (int i = 0; i < 64; i++)
coeffs[i] = coeffs[i] * dequantCoef8x8[group][i] << 4 << qbits;
} else {
for (int i = 0; i < 64; i++)
coeffs[i] = coeffs[i] * dequantCoef8x8[group][i] * scalingList[invZigzag8x8[i]] << qbits;
}
} else {
int qbits = 6 - qp / 6;
int addition = 1 << 5 - qp / 6;
if (scalingList == null) {
for (int i = 0; i < 64; i++)
coeffs[i] = coeffs[i] * (dequantCoef8x8[group][i] << 4) + addition >> qbits;
} else {
for (int i = 0; i < 64; i++)
coeffs[i] = coeffs[i] * dequantCoef8x8[group][i] * scalingList[invZigzag8x8[i]] + addition >> qbits;
}
}
}
public static void idct8x8(int[] ac) {
int off = 0;
for (int row = 0; row < 8; row++) {
int e0 = ac[off] + ac[off + 4];
int e1 = -ac[off + 3] + ac[off + 5] - ac[off + 7] - (ac[off + 7] >> 1);
int e2 = ac[off] - ac[off + 4];
int e3 = ac[off + 1] + ac[off + 7] - ac[off + 3] - (ac[off + 3] >> 1);
int e4 = (ac[off + 2] >> 1) - ac[off + 6];
int e5 = -ac[off + 1] + ac[off + 7] + ac[off + 5] + (ac[off + 5] >> 1);
int e6 = ac[off + 2] + (ac[off + 6] >> 1);
int e7 = ac[off + 3] + ac[off + 5] + ac[off + 1] + (ac[off + 1] >> 1);
int f0 = e0 + e6;
int f1 = e1 + (e7 >> 2);
int f2 = e2 + e4;
int f3 = e3 + (e5 >> 2);
int f4 = e2 - e4;
int f5 = (e3 >> 2) - e5;
int f6 = e0 - e6;
int f7 = e7 - (e1 >> 2);
ac[off] = f0 + f7;
ac[off + 1] = f2 + f5;
ac[off + 2] = f4 + f3;
ac[off + 3] = f6 + f1;
ac[off + 4] = f6 - f1;
ac[off + 5] = f4 - f3;
ac[off + 6] = f2 - f5;
ac[off + 7] = f0 - f7;
off += 8;
}
for (int col = 0; col < 8; col++) {
int e0 = ac[col] + ac[col + 32];
int e1 = -ac[col + 24] + ac[col + 40] - ac[col + 56] - (ac[col + 56] >> 1);
int e2 = ac[col] - ac[col + 32];
int e3 = ac[col + 8] + ac[col + 56] - ac[col + 24] - (ac[col + 24] >> 1);
int e4 = (ac[col + 16] >> 1) - ac[col + 48];
int e5 = -ac[col + 8] + ac[col + 56] + ac[col + 40] + (ac[col + 40] >> 1);
int e6 = ac[col + 16] + (ac[col + 48] >> 1);
int e7 = ac[col + 24] + ac[col + 40] + ac[col + 8] + (ac[col + 8] >> 1);
int f0 = e0 + e6;
int f1 = e1 + (e7 >> 2);
int f2 = e2 + e4;
int f3 = e3 + (e5 >> 2);
int f4 = e2 - e4;
int f5 = (e3 >> 2) - e5;
int f6 = e0 - e6;
int f7 = e7 - (e1 >> 2);
ac[col] = f0 + f7;
ac[col + 8] = f2 + f5;
ac[col + 16] = f4 + f3;
ac[col + 24] = f6 + f1;
ac[col + 32] = f6 - f1;
ac[col + 40] = f4 - f3;
ac[col + 48] = f2 - f5;
ac[col + 56] = f0 - f7;
}
for (int i = 0; i < 64; i++)
ac[i] = ac[i] + 32 >> 6;
}
}

View file

@ -0,0 +1,35 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Utils;
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;
public class DeblockerInput {
public int[][] nCoeff;
public H264Utils.MvList2D mvs;
public MBType[] mbTypes;
public int[][] mbQps;
public boolean[] tr8x8Used;
public Frame[][][] refsUsed;
public SliceHeader[] shs;
public DeblockerInput(SeqParameterSet activeSps) {
int picWidthInMbs = activeSps.picWidthInMbsMinus1 + 1;
int picHeightInMbs = SeqParameterSet.getPicHeightInMbs(activeSps);
this.nCoeff = new int[picHeightInMbs << 2][picWidthInMbs << 2];
this.mvs = new H264Utils.MvList2D(picWidthInMbs << 2, picHeightInMbs << 2);
this.mbTypes = new MBType[picHeightInMbs * picWidthInMbs];
this.tr8x8Used = new boolean[picHeightInMbs * picWidthInMbs];
this.mbQps = new int[3][picHeightInMbs * picWidthInMbs];
this.shs = new SliceHeader[picHeightInMbs * picWidthInMbs];
this.refsUsed = new Frame[picHeightInMbs * picWidthInMbs][][];
}
}

View file

@ -0,0 +1,39 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.ColorSpace;
public class DecoderState {
int[] chromaQpOffset;
int qp;
byte[][] leftRow;
byte[][] topLine;
byte[][] topLeft;
ColorSpace chromaFormat;
H264Utils.MvList mvTop;
H264Utils.MvList mvLeft;
H264Utils.MvList mvTopLeft;
public DecoderState(SliceHeader sh) {
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
this
.chromaQpOffset = new int[] { sh.pps.chromaQpIndexOffset, (sh.pps.extended != null) ? sh.pps.extended.secondChromaQpIndexOffset : sh.pps.chromaQpIndexOffset };
this.chromaFormat = sh.sps.chromaFormatIdc;
this.mvTop = new H264Utils.MvList((mbWidth << 2) + 1);
this.mvLeft = new H264Utils.MvList(4);
this.mvTopLeft = new H264Utils.MvList(1);
this.leftRow = new byte[3][16];
this.topLeft = new byte[3][4];
this.topLine = new byte[3][mbWidth << 4];
this.qp = sh.pps.picInitQpMinus26 + 26 + sh.sliceQpDelta;
}
}

View file

@ -0,0 +1,97 @@
package org.jcodec.codecs.h264.decode;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.codecs.common.biari.MDecoder;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.MapManager;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.CABAC;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.IntObjectMap;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.logging.Logger;
public class FrameReader {
private IntObjectMap<SeqParameterSet> sps = new IntObjectMap<>();
private IntObjectMap<PictureParameterSet> pps = new IntObjectMap<>();
public List<SliceReader> readFrame(List<ByteBuffer> nalUnits) {
List<SliceReader> result = new ArrayList<>();
for (ByteBuffer nalData : nalUnits) {
NALUnit nalUnit = NALUnit.read(nalData);
H264Utils.unescapeNAL(nalData);
if (NALUnitType.SPS == nalUnit.type) {
SeqParameterSet _sps = SeqParameterSet.read(nalData);
this.sps.put(_sps.seqParameterSetId, _sps);
continue;
}
if (NALUnitType.PPS == nalUnit.type) {
PictureParameterSet _pps = PictureParameterSet.read(nalData);
this.pps.put(_pps.picParameterSetId, _pps);
continue;
}
if (NALUnitType.IDR_SLICE == nalUnit.type || NALUnitType.NON_IDR_SLICE == nalUnit.type) {
if (this.sps.size() == 0 || this.pps.size() == 0) {
Logger.warn("Skipping frame as no SPS/PPS have been seen so far...");
return null;
}
result.add(createSliceReader(nalData, nalUnit));
}
}
return result;
}
private SliceReader createSliceReader(ByteBuffer segment, NALUnit nalUnit) {
BitReader _in = BitReader.createBitReader(segment);
SliceHeader sh = SliceHeaderReader.readPart1(_in);
sh.pps = this.pps.get(sh.picParameterSetId);
sh.sps = this.sps.get(sh.pps.seqParameterSetId);
SliceHeaderReader.readPart2(sh, nalUnit, sh.sps, sh.pps, _in);
Mapper mapper = new MapManager(sh.sps, sh.pps).getMapper(sh);
CAVLC[] cavlc = { new CAVLC(sh.sps, sh.pps, 2, 2), new CAVLC(sh.sps, sh.pps, 1, 1), new CAVLC(sh.sps, sh.pps, 1, 1) };
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
CABAC cabac = new CABAC(mbWidth);
MDecoder mDecoder = null;
if (sh.pps.entropyCodingModeFlag) {
_in.terminate();
int[][] cm = new int[2][1024];
int qp = sh.pps.picInitQpMinus26 + 26 + sh.sliceQpDelta;
cabac.initModels(cm, sh.sliceType, sh.cabacInitIdc, qp);
mDecoder = new MDecoder(segment, cm);
}
return new SliceReader(sh.pps, cabac, cavlc, mDecoder, _in, mapper, sh, nalUnit);
}
public void addSpsList(List<ByteBuffer> spsList) {
for (ByteBuffer byteBuffer : spsList)
addSps(byteBuffer);
}
public void addSps(ByteBuffer byteBuffer) {
ByteBuffer clone = NIOUtils.clone(byteBuffer);
H264Utils.unescapeNAL(clone);
SeqParameterSet s = SeqParameterSet.read(clone);
this.sps.put(s.seqParameterSetId, s);
}
public void addPpsList(List<ByteBuffer> ppsList) {
for (ByteBuffer byteBuffer : ppsList)
addPps(byteBuffer);
}
public void addPps(ByteBuffer byteBuffer) {
ByteBuffer clone = NIOUtils.clone(byteBuffer);
H264Utils.unescapeNAL(clone);
PictureParameterSet p = PictureParameterSet.read(clone);
this.pps.put(p.picParameterSetId, p);
}
}

View file

@ -0,0 +1,86 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.common.tools.MathUtil;
public class Intra16x16PredictionBuilder {
public static void predictWithMode(int predMode, int[][] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int x, byte[] pixOut) {
switch (predMode) {
case 0:
predictVertical(residual, topAvailable, topLine, x, pixOut);
break;
case 1:
predictHorizontal(residual, leftAvailable, leftRow, x, pixOut);
break;
case 2:
predictDC(residual, leftAvailable, topAvailable, leftRow, topLine, x, pixOut);
break;
case 3:
predictPlane(residual, leftAvailable, topAvailable, leftRow, topLine, topLeft, x, pixOut);
break;
}
}
public static void predictVertical(int[][] residual, boolean topAvailable, byte[] topLine, int x, byte[] pixOut) {
int off = 0;
for (int j = 0; j < 16; j++) {
for (int i = 0; i < 16; i++, off++)
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.LUMA_4x4_BLOCK_LUT[off]][H264Const.LUMA_4x4_POS_LUT[off]] + topLine[x + i], -128, 127);
}
}
public static void predictHorizontal(int[][] residual, boolean leftAvailable, byte[] leftRow, int x, byte[] pixOut) {
int off = 0;
for (int j = 0; j < 16; j++) {
for (int i = 0; i < 16; i++, off++)
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.LUMA_4x4_BLOCK_LUT[off]][H264Const.LUMA_4x4_POS_LUT[off]] + leftRow[j], -128, 127);
}
}
public static void predictDC(int[][] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, int x, byte[] pixOut) {
int s0;
if (leftAvailable && topAvailable) {
s0 = 0;
for (int k = 0; k < 16; k++)
s0 += leftRow[k];
for (int j = 0; j < 16; j++)
s0 += topLine[x + j];
s0 = s0 + 16 >> 5;
} else if (leftAvailable) {
s0 = 0;
for (int j = 0; j < 16; j++)
s0 += leftRow[j];
s0 = s0 + 8 >> 4;
} else if (topAvailable) {
s0 = 0;
for (int j = 0; j < 16; j++)
s0 += topLine[x + j];
s0 = s0 + 8 >> 4;
} else {
s0 = 0;
}
for (int i = 0; i < 256; i++)
pixOut[i] = (byte)MathUtil.clip(residual[H264Const.LUMA_4x4_BLOCK_LUT[i]][H264Const.LUMA_4x4_POS_LUT[i]] + s0, -128, 127);
}
public static void predictPlane(int[][] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int x, byte[] pixOut) {
int H = 0;
for (int i = 0; i < 7; i++)
H += (i + 1) * (topLine[x + 8 + i] - topLine[x + 6 - i]);
H += 8 * (topLine[x + 15] - topLeft[0]);
int V = 0;
for (int j = 0; j < 7; j++)
V += (j + 1) * (leftRow[8 + j] - leftRow[6 - j]);
V += 8 * (leftRow[15] - topLeft[0]);
int c = 5 * V + 32 >> 6;
int b = 5 * H + 32 >> 6;
int a = 16 * (leftRow[15] + topLine[x + 15]);
int off = 0;
for (int k = 0; k < 16; k++) {
for (int m = 0; m < 16; m++, off++) {
int val = MathUtil.clip(a + b * (m - 7) + c * (k - 7) + 16 >> 5, -128, 127);
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.LUMA_4x4_BLOCK_LUT[off]][H264Const.LUMA_4x4_POS_LUT[off]] + val, -128, 127);
}
}
}
}

View file

@ -0,0 +1,292 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.common.tools.MathUtil;
public class Intra4x4PredictionBuilder {
public static void predictWithMode(int mode, int[] residual, boolean leftAvailable, boolean topAvailable, boolean topRightAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
switch (mode) {
case 0:
predictVertical(residual, topAvailable, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 1:
predictHorizontal(residual, leftAvailable, leftRow, mbOffX, blkX, blkY, pixOut);
break;
case 2:
predictDC(residual, leftAvailable, topAvailable, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 3:
predictDiagonalDownLeft(residual, topAvailable, topRightAvailable, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 4:
predictDiagonalDownRight(residual, leftAvailable, topAvailable, leftRow, topLine, topLeft, mbOffX, blkX, blkY, pixOut);
break;
case 5:
predictVerticalRight(residual, leftAvailable, topAvailable, leftRow, topLine, topLeft, mbOffX, blkX, blkY, pixOut);
break;
case 6:
predictHorizontalDown(residual, leftAvailable, topAvailable, leftRow, topLine, topLeft, mbOffX, blkX, blkY, pixOut);
break;
case 7:
predictVerticalLeft(residual, topAvailable, topRightAvailable, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 8:
predictHorizontalUp(residual, leftAvailable, leftRow, mbOffX, blkX, blkY, pixOut);
break;
}
int oo1 = mbOffX + blkX;
int off1 = (blkY << 4) + blkX + 3;
topLeft[blkY >> 2] = topLine[oo1 + 3];
leftRow[blkY] = pixOut[off1];
leftRow[blkY + 1] = pixOut[off1 + 16];
leftRow[blkY + 2] = pixOut[off1 + 32];
leftRow[blkY + 3] = pixOut[off1 + 48];
int off2 = (blkY << 4) + blkX + 48;
topLine[oo1] = pixOut[off2];
topLine[oo1 + 1] = pixOut[off2 + 1];
topLine[oo1 + 2] = pixOut[off2 + 2];
topLine[oo1 + 3] = pixOut[off2 + 3];
}
public static void predictVertical(int[] residual, boolean topAvailable, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int pixOff = (blkY << 4) + blkX;
int toff = mbOffX + blkX;
int rOff = 0;
for (int j = 0; j < 4; j++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + topLine[toff], -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + topLine[toff + 1], -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + topLine[toff + 2], -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + topLine[toff + 3], -128, 127);
rOff += 4;
pixOff += 16;
}
}
public static void predictHorizontal(int[] residual, boolean leftAvailable, byte[] leftRow, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int pixOff = (blkY << 4) + blkX;
int rOff = 0;
for (int j = 0; j < 4; j++) {
int l = leftRow[blkY + j];
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + l, -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + l, -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + l, -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + l, -128, 127);
rOff += 4;
pixOff += 16;
}
}
public static void predictDC(int[] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int val;
if (leftAvailable && topAvailable) {
val = leftRow[blkY] + leftRow[blkY + 1] + leftRow[blkY + 2] + leftRow[blkY + 3] + topLine[mbOffX + blkX] + topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + topLine[mbOffX + blkX + 3] + 4 >> 3;
} else if (leftAvailable) {
val = leftRow[blkY] + leftRow[blkY + 1] + leftRow[blkY + 2] + leftRow[blkY + 3] + 2 >> 2;
} else if (topAvailable) {
val = topLine[mbOffX + blkX] + topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + topLine[mbOffX + blkX + 3] + 2 >> 2;
} else {
val = 0;
}
int pixOff = (blkY << 4) + blkX;
int rOff = 0;
for (int j = 0; j < 4; j++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + val, -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + val, -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + val, -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + val, -128, 127);
pixOff += 16;
rOff += 4;
}
}
public static void predictDiagonalDownLeft(int[] residual, boolean topAvailable, boolean topRightAvailable, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int to = mbOffX + blkX;
int tr0 = topLine[to + 3], tr1 = topLine[to + 3], tr2 = topLine[to + 3], tr3 = topLine[to + 3];
if (topRightAvailable) {
tr0 = topLine[to + 4];
tr1 = topLine[to + 5];
tr2 = topLine[to + 6];
tr3 = topLine[to + 7];
}
int c0 = topLine[to] + topLine[to + 2] + (topLine[to + 1] << 1) + 2 >> 2;
int c1 = topLine[to + 1] + topLine[to + 3] + (topLine[to + 2] << 1) + 2 >> 2;
int c2 = topLine[to + 2] + tr0 + (topLine[to + 3] << 1) + 2 >> 2;
int c3 = topLine[to + 3] + tr1 + (tr0 << 1) + 2 >> 2;
int c4 = tr0 + tr2 + (tr1 << 1) + 2 >> 2;
int c5 = tr1 + tr3 + (tr2 << 1) + 2 >> 2;
int c6 = tr2 + 3 * tr3 + 2 >> 2;
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c1, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c2, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c3, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c4, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c2, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c3, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c4, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c5, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c3, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c4, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c5, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c6, -128, 127);
}
public static void predictDiagonalDownRight(int[] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int off = (blkY << 4) + blkX;
int c0 = topLine[mbOffX + blkX] + 2 * topLeft[blkY >> 2] + leftRow[blkY] + 2 >> 2;
int c1 = topLeft[blkY >> 2] + (topLine[mbOffX + blkX + 0] << 1) + topLine[mbOffX + blkX + 1] + 2 >> 2;
int c2 = topLine[mbOffX + blkX] + (topLine[mbOffX + blkX + 1] << 1) + topLine[mbOffX + blkX + 2] + 2 >> 2;
int c3 = topLine[mbOffX + blkX + 1] + (topLine[mbOffX + blkX + 2] << 1) + topLine[mbOffX + blkX + 3] + 2 >> 2;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
int c4 = topLeft[blkY >> 2] + (leftRow[blkY] << 1) + leftRow[blkY + 1] + 2 >> 2;
int c6 = topLeft[blkY >> 2] + (topLine[mbOffX + blkX] << 1) + topLine[mbOffX + blkX + 1] + 2 >> 2;
int c7 = topLine[mbOffX + blkX] + (topLine[mbOffX + blkX + 1] << 1) + topLine[mbOffX + blkX + 2] + 2 >> 2;
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c4, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c0, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c6, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c7, -128, 127);
int c8 = leftRow[blkY + 0] + (leftRow[blkY + 1] << 1) + leftRow[blkY + 2] + 2 >> 2;
int c9 = topLeft[blkY >> 2] + (leftRow[blkY] << 1) + leftRow[blkY + 1] + 2 >> 2;
int c11 = topLeft[blkY >> 2] + (topLine[mbOffX + blkX] << 1) + topLine[mbOffX + blkX + 1] + 2 >> 2;
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c8, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c9, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c0, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c11, -128, 127);
int c12 = leftRow[blkY + 1] + (leftRow[blkY + 2] << 1) + leftRow[blkY + 3] + 2 >> 2;
int c13 = leftRow[blkY] + (leftRow[blkY + 1] << 1) + leftRow[blkY + 2] + 2 >> 2;
int c14 = topLeft[blkY >> 2] + (leftRow[blkY] << 1) + leftRow[blkY + 1] + 2 >> 2;
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c12, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c13, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c14, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c0, -128, 127);
}
public static void predictVerticalRight(int[] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int v1 = topLeft[blkY >> 2] + topLine[mbOffX + blkX + 0] + 1 >> 1;
int v2 = topLine[mbOffX + blkX + 0] + topLine[mbOffX + blkX + 1] + 1 >> 1;
int v3 = topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + 1 >> 1;
int v4 = topLine[mbOffX + blkX + 2] + topLine[mbOffX + blkX + 3] + 1 >> 1;
int v5 = leftRow[blkY] + 2 * topLeft[blkY >> 2] + topLine[mbOffX + blkX + 0] + 2 >> 2;
int v6 = topLeft[blkY >> 2] + 2 * topLine[mbOffX + blkX + 0] + topLine[mbOffX + blkX + 1] + 2 >> 2;
int v7 = topLine[mbOffX + blkX + 0] + 2 * topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + 2 >> 2;
int v8 = topLine[mbOffX + blkX + 1] + 2 * topLine[mbOffX + blkX + 2] + topLine[mbOffX + blkX + 3] + 2 >> 2;
int v9 = topLeft[blkY >> 2] + 2 * leftRow[blkY] + leftRow[blkY + 1] + 2 >> 2;
int v10 = leftRow[blkY] + 2 * leftRow[blkY + 1] + leftRow[blkY + 2] + 2 >> 2;
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + v1, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + v2, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + v3, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + v4, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + v5, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + v6, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + v7, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + v8, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + v9, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + v1, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + v2, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + v3, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + v10, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + v5, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + v6, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + v7, -128, 127);
}
public static void predictHorizontalDown(int[] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int c0 = topLeft[blkY >> 2] + leftRow[blkY] + 1 >> 1;
int c1 = leftRow[blkY] + 2 * topLeft[blkY >> 2] + topLine[mbOffX + blkX + 0] + 2 >> 2;
int c2 = topLeft[blkY >> 2] + 2 * topLine[mbOffX + blkX + 0] + topLine[mbOffX + blkX + 1] + 2 >> 2;
int c3 = topLine[mbOffX + blkX + 0] + 2 * topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + 2 >> 2;
int c4 = leftRow[blkY] + leftRow[blkY + 1] + 1 >> 1;
int c5 = topLeft[blkY >> 2] + 2 * leftRow[blkY] + leftRow[blkY + 1] + 2 >> 2;
int c6 = leftRow[blkY + 1] + leftRow[blkY + 2] + 1 >> 1;
int c7 = leftRow[blkY] + 2 * leftRow[blkY + 1] + leftRow[blkY + 2] + 2 >> 2;
int c8 = leftRow[blkY + 2] + leftRow[blkY + 3] + 1 >> 1;
int c9 = leftRow[blkY + 1] + 2 * leftRow[blkY + 2] + leftRow[blkY + 3] + 2 >> 2;
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c4, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c5, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c0, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c1, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c6, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c7, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c4, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c5, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c8, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c9, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c6, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c7, -128, 127);
}
public static void predictVerticalLeft(int[] residual, boolean topAvailable, boolean topRightAvailable, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int to = mbOffX + blkX;
int tr0 = topLine[to + 3], tr1 = topLine[to + 3], tr2 = topLine[to + 3];
if (topRightAvailable) {
tr0 = topLine[to + 4];
tr1 = topLine[to + 5];
tr2 = topLine[to + 6];
}
int c0 = topLine[to] + topLine[to + 1] + 1 >> 1;
int c1 = topLine[to + 1] + topLine[to + 2] + 1 >> 1;
int c2 = topLine[to + 2] + topLine[to + 3] + 1 >> 1;
int c3 = topLine[to + 3] + tr0 + 1 >> 1;
int c4 = tr0 + tr1 + 1 >> 1;
int c5 = topLine[to] + 2 * topLine[to + 1] + topLine[to + 2] + 2 >> 2;
int c6 = topLine[to + 1] + 2 * topLine[to + 2] + topLine[to + 3] + 2 >> 2;
int c7 = topLine[to + 2] + 2 * topLine[to + 3] + tr0 + 2 >> 2;
int c8 = topLine[to + 3] + 2 * tr0 + tr1 + 2 >> 2;
int c9 = tr0 + 2 * tr1 + tr2 + 2 >> 2;
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c5, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c6, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c7, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c8, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c1, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c2, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c3, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c4, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c6, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c7, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c8, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c9, -128, 127);
}
public static void predictHorizontalUp(int[] residual, boolean leftAvailable, byte[] leftRow, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int c0 = leftRow[blkY] + leftRow[blkY + 1] + 1 >> 1;
int c1 = leftRow[blkY] + (leftRow[blkY + 1] << 1) + leftRow[blkY + 2] + 2 >> 2;
int c2 = leftRow[blkY + 1] + leftRow[blkY + 2] + 1 >> 1;
int c3 = leftRow[blkY + 1] + (leftRow[blkY + 2] << 1) + leftRow[blkY + 3] + 2 >> 2;
int c4 = leftRow[blkY + 2] + leftRow[blkY + 3] + 1 >> 1;
int c5 = leftRow[blkY + 2] + (leftRow[blkY + 3] << 1) + leftRow[blkY + 3] + 2 >> 2;
int c6 = leftRow[blkY + 3];
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c2, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c3, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c4, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c5, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c4, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c5, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c6, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c6, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c6, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c6, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c6, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c6, -128, 127);
}
}

View file

@ -0,0 +1,383 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.common.Preconditions;
import org.jcodec.common.tools.MathUtil;
public class Intra8x8PredictionBuilder {
byte[] topBuf = new byte[16];
byte[] leftBuf = new byte[8];
byte[] genBuf = new byte[24];
public void predictWithMode(int mode, int[] residual, boolean leftAvailable, boolean topAvailable, boolean topLeftAvailable, boolean topRightAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
switch (mode) {
case 0:
Preconditions.checkState(topAvailable, "");
predictVertical(residual, topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 1:
Preconditions.checkState(leftAvailable, "");
predictHorizontal(residual, topLeftAvailable, topLeft, leftRow, mbOffX, blkX, blkY, pixOut);
break;
case 2:
predictDC(residual, topLeftAvailable, topRightAvailable, leftAvailable, topAvailable, topLeft, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 3:
Preconditions.checkState(topAvailable, "");
predictDiagonalDownLeft(residual, topLeftAvailable, topAvailable, topRightAvailable, topLeft, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 4:
Preconditions.checkState((topAvailable && leftAvailable && topLeftAvailable), "");
predictDiagonalDownRight(residual, topRightAvailable, topLeft, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 5:
Preconditions.checkState((topAvailable && leftAvailable && topLeftAvailable), "");
predictVerticalRight(residual, topRightAvailable, topLeft, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 6:
Preconditions.checkState((topAvailable && leftAvailable && topLeftAvailable), "");
predictHorizontalDown(residual, topRightAvailable, topLeft, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 7:
Preconditions.checkState(topAvailable, "");
predictVerticalLeft(residual, topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 8:
Preconditions.checkState(leftAvailable, "");
predictHorizontalUp(residual, topLeftAvailable, topLeft, leftRow, mbOffX, blkX, blkY, pixOut);
break;
}
int oo1 = mbOffX + blkX;
int off1 = (blkY << 4) + blkX + 7;
topLeft[blkY >> 2] = topLine[oo1 + 7];
for (int i = 0; i < 8; i++)
leftRow[blkY + i] = pixOut[off1 + (i << 4)];
int off2 = (blkY << 4) + blkX + 112;
for (int j = 0; j < 8; j++)
topLine[oo1 + j] = pixOut[off2 + j];
topLeft[(blkY >> 2) + 1] = leftRow[blkY + 3];
}
private void interpolateTop(boolean topLeftAvailable, boolean topRightAvailable, byte[] topLeft, byte[] topLine, int blkX, int blkY, byte[] out) {
int a = topLeftAvailable ? topLeft[blkY >> 2] : topLine[blkX];
out[0] = (byte)(a + (topLine[blkX] << 1) + topLine[blkX + 1] + 2 >> 2);
int i;
for (i = 1; i < 7; i++)
out[i] = (byte)(topLine[blkX + i - 1] + (topLine[blkX + i] << 1) + topLine[blkX + i + 1] + 2 >> 2);
if (topRightAvailable) {
for (; i < 15; i++)
out[i] = (byte)(topLine[blkX + i - 1] + (topLine[blkX + i] << 1) + topLine[blkX + i + 1] + 2 >> 2);
out[15] = (byte)(topLine[blkX + 14] + (topLine[blkX + 15] << 1) + topLine[blkX + 15] + 2 >> 2);
} else {
out[7] = (byte)(topLine[blkX + 6] + (topLine[blkX + 7] << 1) + topLine[blkX + 7] + 2 >> 2);
for (int j = 8; j < 16; j++)
out[j] = topLine[blkX + 7];
}
}
private void interpolateLeft(boolean topLeftAvailable, byte[] topLeft, byte[] leftRow, int blkY, byte[] out) {
int a = topLeftAvailable ? topLeft[blkY >> 2] : leftRow[0];
out[0] = (byte)(a + (leftRow[blkY] << 1) + leftRow[blkY + 1] + 2 >> 2);
for (int i = 1; i < 7; i++)
out[i] = (byte)(leftRow[blkY + i - 1] + (leftRow[blkY + i] << 1) + leftRow[blkY + i + 1] + 2 >> 2);
out[7] = (byte)(leftRow[blkY + 6] + (leftRow[blkY + 7] << 1) + leftRow[blkY + 7] + 2 >> 2);
}
private int interpolateTopLeft(boolean topAvailable, boolean leftAvailable, byte[] topLeft, byte[] topLine, byte[] leftRow, int mbOffX, int blkX, int blkY) {
int a = topLeft[blkY >> 2];
int b = topAvailable ? topLine[mbOffX + blkX] : a;
int c = leftAvailable ? leftRow[blkY] : a;
int aa = a << 1;
return aa + b + c + 2 >> 2;
}
public void copyAdd(byte[] pred, int srcOff, int[] residual, int pixOff, int rOff, byte[] out) {
out[pixOff] = (byte)MathUtil.clip(residual[rOff] + pred[srcOff], -128, 127);
out[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + pred[srcOff + 1], -128, 127);
out[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + pred[srcOff + 2], -128, 127);
out[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + pred[srcOff + 3], -128, 127);
out[pixOff + 4] = (byte)MathUtil.clip(residual[rOff + 4] + pred[srcOff + 4], -128, 127);
out[pixOff + 5] = (byte)MathUtil.clip(residual[rOff + 5] + pred[srcOff + 5], -128, 127);
out[pixOff + 6] = (byte)MathUtil.clip(residual[rOff + 6] + pred[srcOff + 6], -128, 127);
out[pixOff + 7] = (byte)MathUtil.clip(residual[rOff + 7] + pred[srcOff + 7], -128, 127);
}
public void fillAdd(int[] residual, int pixOff, int val, byte[] pixOut) {
int rOff = 0;
for (int i = 0; i < 8; i++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + val, -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + val, -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + val, -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + val, -128, 127);
pixOut[pixOff + 4] = (byte)MathUtil.clip(residual[rOff + 4] + val, -128, 127);
pixOut[pixOff + 5] = (byte)MathUtil.clip(residual[rOff + 5] + val, -128, 127);
pixOut[pixOff + 6] = (byte)MathUtil.clip(residual[rOff + 6] + val, -128, 127);
pixOut[pixOff + 7] = (byte)MathUtil.clip(residual[rOff + 7] + val, -128, 127);
pixOff += 16;
rOff += 8;
}
}
public void predictVertical(int[] residual, boolean topLeftAvailable, boolean topRightAvailable, byte[] topLeft, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
int pixOff = (blkY << 4) + blkX;
int rOff = 0;
for (int i = 0; i < 8; i++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + this.topBuf[0], -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + this.topBuf[1], -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + this.topBuf[2], -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + this.topBuf[3], -128, 127);
pixOut[pixOff + 4] = (byte)MathUtil.clip(residual[rOff + 4] + this.topBuf[4], -128, 127);
pixOut[pixOff + 5] = (byte)MathUtil.clip(residual[rOff + 5] + this.topBuf[5], -128, 127);
pixOut[pixOff + 6] = (byte)MathUtil.clip(residual[rOff + 6] + this.topBuf[6], -128, 127);
pixOut[pixOff + 7] = (byte)MathUtil.clip(residual[rOff + 7] + this.topBuf[7], -128, 127);
pixOff += 16;
rOff += 8;
}
}
public void predictHorizontal(int[] residual, boolean topLeftAvailable, byte[] topLeft, byte[] leftRow, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateLeft(topLeftAvailable, topLeft, leftRow, blkY, this.leftBuf);
int pixOff = (blkY << 4) + blkX;
int rOff = 0;
for (int i = 0; i < 8; i++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 4] = (byte)MathUtil.clip(residual[rOff + 4] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 5] = (byte)MathUtil.clip(residual[rOff + 5] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 6] = (byte)MathUtil.clip(residual[rOff + 6] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 7] = (byte)MathUtil.clip(residual[rOff + 7] + this.leftBuf[i], -128, 127);
pixOff += 16;
rOff += 8;
}
}
public void predictDC(int[] residual, boolean topLeftAvailable, boolean topRightAvailable, boolean leftAvailable, boolean topAvailable, byte[] topLeft, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
if (topAvailable && leftAvailable) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
interpolateLeft(topLeftAvailable, topLeft, leftRow, blkY, this.leftBuf);
int sum1 = this.topBuf[0] + this.topBuf[1] + this.topBuf[2] + this.topBuf[3];
int sum2 = this.topBuf[4] + this.topBuf[5] + this.topBuf[6] + this.topBuf[7];
int sum3 = this.leftBuf[0] + this.leftBuf[1] + this.leftBuf[2] + this.leftBuf[3];
int sum4 = this.leftBuf[4] + this.leftBuf[5] + this.leftBuf[6] + this.leftBuf[7];
fillAdd(residual, (blkY << 4) + blkX, sum1 + sum2 + sum3 + sum4 + 8 >> 4, pixOut);
} else if (leftAvailable) {
interpolateLeft(topLeftAvailable, topLeft, leftRow, blkY, this.leftBuf);
int sum3 = this.leftBuf[0] + this.leftBuf[1] + this.leftBuf[2] + this.leftBuf[3];
int sum4 = this.leftBuf[4] + this.leftBuf[5] + this.leftBuf[6] + this.leftBuf[7];
fillAdd(residual, (blkY << 4) + blkX, sum3 + sum4 + 4 >> 3, pixOut);
} else if (topAvailable) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
int sum1 = this.topBuf[0] + this.topBuf[1] + this.topBuf[2] + this.topBuf[3];
int sum2 = this.topBuf[4] + this.topBuf[5] + this.topBuf[6] + this.topBuf[7];
fillAdd(residual, (blkY << 4) + blkX, sum1 + sum2 + 4 >> 3, pixOut);
} else {
fillAdd(residual, (blkY << 4) + blkX, 0, pixOut);
}
}
public void predictDiagonalDownLeft(int[] residual, boolean topLeftAvailable, boolean topAvailable, boolean topRightAvailable, byte[] topLeft, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
this.genBuf[0] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[1] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[3] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[4] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
this.genBuf[5] = (byte)(this.topBuf[5] + this.topBuf[7] + (this.topBuf[6] << 1) + 2 >> 2);
this.genBuf[6] = (byte)(this.topBuf[6] + this.topBuf[8] + (this.topBuf[7] << 1) + 2 >> 2);
this.genBuf[7] = (byte)(this.topBuf[7] + this.topBuf[9] + (this.topBuf[8] << 1) + 2 >> 2);
this.genBuf[8] = (byte)(this.topBuf[8] + this.topBuf[10] + (this.topBuf[9] << 1) + 2 >> 2);
this.genBuf[9] = (byte)(this.topBuf[9] + this.topBuf[11] + (this.topBuf[10] << 1) + 2 >> 2);
this.genBuf[10] = (byte)(this.topBuf[10] + this.topBuf[12] + (this.topBuf[11] << 1) + 2 >> 2);
this.genBuf[11] = (byte)(this.topBuf[11] + this.topBuf[13] + (this.topBuf[12] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.topBuf[12] + this.topBuf[14] + (this.topBuf[13] << 1) + 2 >> 2);
this.genBuf[13] = (byte)(this.topBuf[13] + this.topBuf[15] + (this.topBuf[14] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.topBuf[14] + this.topBuf[15] + (this.topBuf[15] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 0, residual, off, 0, pixOut);
copyAdd(this.genBuf, 1, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 2, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 3, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 4, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 5, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 6, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 7, residual, off + 112, 56, pixOut);
}
public void predictDiagonalDownRight(int[] residual, boolean topRightAvailable, byte[] topLeft, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(true, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
interpolateLeft(true, topLeft, leftRow, blkY, this.leftBuf);
int tl = interpolateTopLeft(true, true, topLeft, topLine, leftRow, mbOffX, blkX, blkY);
this.genBuf[0] = (byte)(this.leftBuf[7] + this.leftBuf[5] + (this.leftBuf[6] << 1) + 2 >> 2);
this.genBuf[1] = (byte)(this.leftBuf[6] + this.leftBuf[4] + (this.leftBuf[5] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.leftBuf[5] + this.leftBuf[3] + (this.leftBuf[4] << 1) + 2 >> 2);
this.genBuf[3] = (byte)(this.leftBuf[4] + this.leftBuf[2] + (this.leftBuf[3] << 1) + 2 >> 2);
this.genBuf[4] = (byte)(this.leftBuf[3] + this.leftBuf[1] + (this.leftBuf[2] << 1) + 2 >> 2);
this.genBuf[5] = (byte)(this.leftBuf[2] + this.leftBuf[0] + (this.leftBuf[1] << 1) + 2 >> 2);
this.genBuf[6] = (byte)(this.leftBuf[1] + tl + (this.leftBuf[0] << 1) + 2 >> 2);
this.genBuf[7] = (byte)(this.leftBuf[0] + this.topBuf[0] + (tl << 1) + 2 >> 2);
this.genBuf[8] = (byte)(tl + this.topBuf[1] + (this.topBuf[0] << 1) + 2 >> 2);
this.genBuf[9] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[10] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[11] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[13] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.topBuf[5] + this.topBuf[7] + (this.topBuf[6] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 7, residual, off, 0, pixOut);
copyAdd(this.genBuf, 6, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 5, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 4, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 3, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 2, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 1, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 0, residual, off + 112, 56, pixOut);
}
public void predictVerticalRight(int[] residual, boolean topRightAvailable, byte[] topLeft, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(true, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
interpolateLeft(true, topLeft, leftRow, blkY, this.leftBuf);
int tl = interpolateTopLeft(true, true, topLeft, topLine, leftRow, mbOffX, blkX, blkY);
this.genBuf[0] = (byte)(this.leftBuf[5] + this.leftBuf[3] + (this.leftBuf[4] << 1) + 2 >> 2);
this.genBuf[1] = (byte)(this.leftBuf[3] + this.leftBuf[1] + (this.leftBuf[2] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.leftBuf[1] + tl + (this.leftBuf[0] << 1) + 2 >> 2);
this.genBuf[3] = (byte)(tl + this.topBuf[0] + 1 >> 1);
this.genBuf[4] = (byte)(this.topBuf[0] + this.topBuf[1] + 1 >> 1);
this.genBuf[5] = (byte)(this.topBuf[1] + this.topBuf[2] + 1 >> 1);
this.genBuf[6] = (byte)(this.topBuf[2] + this.topBuf[3] + 1 >> 1);
this.genBuf[7] = (byte)(this.topBuf[3] + this.topBuf[4] + 1 >> 1);
this.genBuf[8] = (byte)(this.topBuf[4] + this.topBuf[5] + 1 >> 1);
this.genBuf[9] = (byte)(this.topBuf[5] + this.topBuf[6] + 1 >> 1);
this.genBuf[10] = (byte)(this.topBuf[6] + this.topBuf[7] + 1 >> 1);
this.genBuf[11] = (byte)(this.leftBuf[6] + this.leftBuf[4] + (this.leftBuf[5] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.leftBuf[4] + this.leftBuf[2] + (this.leftBuf[3] << 1) + 2 >> 2);
this.genBuf[13] = (byte)(this.leftBuf[2] + this.leftBuf[0] + (this.leftBuf[1] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.leftBuf[0] + this.topBuf[0] + (tl << 1) + 2 >> 2);
this.genBuf[15] = (byte)(tl + this.topBuf[1] + (this.topBuf[0] << 1) + 2 >> 2);
this.genBuf[16] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[17] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[18] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[19] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[20] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
this.genBuf[21] = (byte)(this.topBuf[5] + this.topBuf[7] + (this.topBuf[6] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 3, residual, off, 0, pixOut);
copyAdd(this.genBuf, 14, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 2, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 13, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 1, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 12, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 0, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 11, residual, off + 112, 56, pixOut);
}
public void predictHorizontalDown(int[] residual, boolean topRightAvailable, byte[] topLeft, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(true, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
interpolateLeft(true, topLeft, leftRow, blkY, this.leftBuf);
int tl = interpolateTopLeft(true, true, topLeft, topLine, leftRow, mbOffX, blkX, blkY);
this.genBuf[0] = (byte)(this.leftBuf[7] + this.leftBuf[6] + 1 >> 1);
this.genBuf[1] = (byte)(this.leftBuf[5] + this.leftBuf[7] + (this.leftBuf[6] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.leftBuf[6] + this.leftBuf[5] + 1 >> 1);
this.genBuf[3] = (byte)(this.leftBuf[4] + this.leftBuf[6] + (this.leftBuf[5] << 1) + 2 >> 2);
this.genBuf[4] = (byte)(this.leftBuf[5] + this.leftBuf[4] + 1 >> 1);
this.genBuf[5] = (byte)(this.leftBuf[3] + this.leftBuf[5] + (this.leftBuf[4] << 1) + 2 >> 2);
this.genBuf[6] = (byte)(this.leftBuf[4] + this.leftBuf[3] + 1 >> 1);
this.genBuf[7] = (byte)(this.leftBuf[2] + this.leftBuf[4] + (this.leftBuf[3] << 1) + 2 >> 2);
this.genBuf[8] = (byte)(this.leftBuf[3] + this.leftBuf[2] + 1 >> 1);
this.genBuf[9] = (byte)(this.leftBuf[1] + this.leftBuf[3] + (this.leftBuf[2] << 1) + 2 >> 2);
this.genBuf[10] = (byte)(this.leftBuf[2] + this.leftBuf[1] + 1 >> 1);
this.genBuf[11] = (byte)(this.leftBuf[0] + this.leftBuf[2] + (this.leftBuf[1] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.leftBuf[1] + this.leftBuf[0] + 1 >> 1);
this.genBuf[13] = (byte)(tl + this.leftBuf[1] + (this.leftBuf[0] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.leftBuf[0] + tl + 1 >> 1);
this.genBuf[15] = (byte)(this.leftBuf[0] + this.topBuf[0] + (tl << 1) + 2 >> 2);
this.genBuf[16] = (byte)(tl + this.topBuf[1] + (this.topBuf[0] << 1) + 2 >> 2);
this.genBuf[17] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[18] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[19] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[20] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[21] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 14, residual, off, 0, pixOut);
copyAdd(this.genBuf, 12, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 10, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 8, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 6, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 4, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 2, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 0, residual, off + 112, 56, pixOut);
}
public void predictVerticalLeft(int[] residual, boolean topLeftAvailable, boolean topRightAvailable, byte[] topLeft, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
this.genBuf[0] = (byte)(this.topBuf[0] + this.topBuf[1] + 1 >> 1);
this.genBuf[1] = (byte)(this.topBuf[1] + this.topBuf[2] + 1 >> 1);
this.genBuf[2] = (byte)(this.topBuf[2] + this.topBuf[3] + 1 >> 1);
this.genBuf[3] = (byte)(this.topBuf[3] + this.topBuf[4] + 1 >> 1);
this.genBuf[4] = (byte)(this.topBuf[4] + this.topBuf[5] + 1 >> 1);
this.genBuf[5] = (byte)(this.topBuf[5] + this.topBuf[6] + 1 >> 1);
this.genBuf[6] = (byte)(this.topBuf[6] + this.topBuf[7] + 1 >> 1);
this.genBuf[7] = (byte)(this.topBuf[7] + this.topBuf[8] + 1 >> 1);
this.genBuf[8] = (byte)(this.topBuf[8] + this.topBuf[9] + 1 >> 1);
this.genBuf[9] = (byte)(this.topBuf[9] + this.topBuf[10] + 1 >> 1);
this.genBuf[10] = (byte)(this.topBuf[10] + this.topBuf[11] + 1 >> 1);
this.genBuf[11] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[13] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[15] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
this.genBuf[16] = (byte)(this.topBuf[5] + this.topBuf[7] + (this.topBuf[6] << 1) + 2 >> 2);
this.genBuf[17] = (byte)(this.topBuf[6] + this.topBuf[8] + (this.topBuf[7] << 1) + 2 >> 2);
this.genBuf[18] = (byte)(this.topBuf[7] + this.topBuf[9] + (this.topBuf[8] << 1) + 2 >> 2);
this.genBuf[19] = (byte)(this.topBuf[8] + this.topBuf[10] + (this.topBuf[9] << 1) + 2 >> 2);
this.genBuf[20] = (byte)(this.topBuf[9] + this.topBuf[11] + (this.topBuf[10] << 1) + 2 >> 2);
this.genBuf[21] = (byte)(this.topBuf[10] + this.topBuf[12] + (this.topBuf[11] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 0, residual, off, 0, pixOut);
copyAdd(this.genBuf, 11, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 1, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 12, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 2, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 13, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 3, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 14, residual, off + 112, 56, pixOut);
}
public void predictHorizontalUp(int[] residual, boolean topLeftAvailable, byte[] topLeft, byte[] leftRow, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateLeft(topLeftAvailable, topLeft, leftRow, blkY, this.leftBuf);
this.genBuf[0] = (byte)(this.leftBuf[0] + this.leftBuf[1] + 1 >> 1);
this.genBuf[1] = (byte)(this.leftBuf[2] + this.leftBuf[0] + (this.leftBuf[1] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.leftBuf[1] + this.leftBuf[2] + 1 >> 1);
this.genBuf[3] = (byte)(this.leftBuf[3] + this.leftBuf[1] + (this.leftBuf[2] << 1) + 2 >> 2);
this.genBuf[4] = (byte)(this.leftBuf[2] + this.leftBuf[3] + 1 >> 1);
this.genBuf[5] = (byte)(this.leftBuf[4] + this.leftBuf[2] + (this.leftBuf[3] << 1) + 2 >> 2);
this.genBuf[6] = (byte)(this.leftBuf[3] + this.leftBuf[4] + 1 >> 1);
this.genBuf[7] = (byte)(this.leftBuf[5] + this.leftBuf[3] + (this.leftBuf[4] << 1) + 2 >> 2);
this.genBuf[8] = (byte)(this.leftBuf[4] + this.leftBuf[5] + 1 >> 1);
this.genBuf[9] = (byte)(this.leftBuf[6] + this.leftBuf[4] + (this.leftBuf[5] << 1) + 2 >> 2);
this.genBuf[10] = (byte)(this.leftBuf[5] + this.leftBuf[6] + 1 >> 1);
this.genBuf[11] = (byte)(this.leftBuf[7] + this.leftBuf[5] + (this.leftBuf[6] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.leftBuf[6] + this.leftBuf[7] + 1 >> 1);
this.genBuf[13] = (byte)(this.leftBuf[6] + this.leftBuf[7] + (this.leftBuf[7] << 1) + 2 >> 2);
this.genBuf[21] = this.leftBuf[7];
this.genBuf[20] = this.leftBuf[7];
this.genBuf[19] = this.leftBuf[7];
this.genBuf[18] = this.leftBuf[7];
this.genBuf[17] = this.leftBuf[7];
this.genBuf[16] = this.leftBuf[7];
this.genBuf[15] = this.leftBuf[7];
this.genBuf[14] = this.leftBuf[7];
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 0, residual, off, 0, pixOut);
copyAdd(this.genBuf, 2, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 4, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 6, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 8, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 10, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 12, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 14, residual, off + 112, 56, pixOut);
}
}

View file

@ -0,0 +1,277 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.common.model.ColorSpace;
public class MBlock {
public int chromaPredictionMode;
public int mbQPDelta;
public int[] dc;
public int[][][] ac;
public boolean transform8x8Used;
public int[] lumaModes;
public int[] dc1;
public int[] dc2;
public int _cbp;
public int mbType;
public MBType curMbType;
public PB16x16 pb16x16;
public PB168x168 pb168x168;
public PB8x8 pb8x8;
public IPCM ipcm;
public int mbIdx;
public boolean fieldDecoding;
public MBType prevMbType;
public int luma16x16Mode;
public H264Utils.MvList x;
public H264Const.PartPred[] partPreds;
public boolean skipped;
public int[] nCoeff;
public MBlock(ColorSpace chromaFormat) {
this.pb8x8 = new PB8x8();
this.pb16x16 = new PB16x16();
this.pb168x168 = new PB168x168();
this.dc = new int[16];
this.ac = new int[][][] { new int[16][64], new int[4][16], new int[4][16] };
this.lumaModes = new int[16];
this.nCoeff = new int[16];
this.dc1 = new int[16 >> chromaFormat.compWidth[1] >> chromaFormat.compHeight[1]];
this.dc2 = new int[16 >> chromaFormat.compWidth[2] >> chromaFormat.compHeight[2]];
this.ipcm = new IPCM(chromaFormat);
this.x = new H264Utils.MvList(16);
this.partPreds = new H264Const.PartPred[4];
}
public int cbpLuma() {
return this._cbp & 0xF;
}
public int cbpChroma() {
return this._cbp >> 4;
}
public void cbp(int cbpLuma, int cbpChroma) {
this._cbp = cbpLuma & 0xF | cbpChroma << 4;
}
static class PB16x16 {
public int[] refIdx = new int[2];
public int[] mvdX = new int[2];
public int[] mvdY = new int[2];
public void clean() {
this.refIdx[1] = 0;
this.refIdx[0] = 0;
this.mvdX[1] = 0;
this.mvdX[0] = 0;
this.mvdY[1] = 0;
this.mvdY[0] = 0;
}
}
static class PB168x168 {
public int[] refIdx1 = new int[2];
public int[] refIdx2 = new int[2];
public int[] mvdX1 = new int[2];
public int[] mvdY1 = new int[2];
public int[] mvdX2 = new int[2];
public int[] mvdY2 = new int[2];
public void clean() {
this.refIdx1[1] = 0;
this.refIdx1[0] = 0;
this.refIdx2[1] = 0;
this.refIdx2[0] = 0;
this.mvdX1[1] = 0;
this.mvdX1[0] = 0;
this.mvdY1[1] = 0;
this.mvdY1[0] = 0;
this.mvdX2[1] = 0;
this.mvdX2[0] = 0;
this.mvdY2[1] = 0;
this.mvdY2[0] = 0;
}
}
static class PB8x8 {
public int[][] refIdx = new int[2][4];
public int[] subMbTypes = new int[4];
public int[][] mvdX1 = new int[2][4];
public int[][] mvdY1 = new int[2][4];
public int[][] mvdX2 = new int[2][4];
public int[][] mvdY2 = new int[2][4];
public int[][] mvdX3 = new int[2][4];
public int[][] mvdY3 = new int[2][4];
public int[][] mvdX4 = new int[2][4];
public int[][] mvdY4 = new int[2][4];
public void clean() {
this.mvdX1[0][3] = 0;
this.mvdX1[0][2] = 0;
this.mvdX1[0][1] = 0;
this.mvdX1[0][0] = 0;
this.mvdX2[0][3] = 0;
this.mvdX2[0][2] = 0;
this.mvdX2[0][1] = 0;
this.mvdX2[0][0] = 0;
this.mvdX3[0][3] = 0;
this.mvdX3[0][2] = 0;
this.mvdX3[0][1] = 0;
this.mvdX3[0][0] = 0;
this.mvdX4[0][3] = 0;
this.mvdX4[0][2] = 0;
this.mvdX4[0][1] = 0;
this.mvdX4[0][0] = 0;
this.mvdY1[0][3] = 0;
this.mvdY1[0][2] = 0;
this.mvdY1[0][1] = 0;
this.mvdY1[0][0] = 0;
this.mvdY2[0][3] = 0;
this.mvdY2[0][2] = 0;
this.mvdY2[0][1] = 0;
this.mvdY2[0][0] = 0;
this.mvdY3[0][3] = 0;
this.mvdY3[0][2] = 0;
this.mvdY3[0][1] = 0;
this.mvdY3[0][0] = 0;
this.mvdY4[0][3] = 0;
this.mvdY4[0][2] = 0;
this.mvdY4[0][1] = 0;
this.mvdY4[0][0] = 0;
this.mvdX1[1][3] = 0;
this.mvdX1[1][2] = 0;
this.mvdX1[1][1] = 0;
this.mvdX1[1][0] = 0;
this.mvdX2[1][3] = 0;
this.mvdX2[1][2] = 0;
this.mvdX2[1][1] = 0;
this.mvdX2[1][0] = 0;
this.mvdX3[1][3] = 0;
this.mvdX3[1][2] = 0;
this.mvdX3[1][1] = 0;
this.mvdX3[1][0] = 0;
this.mvdX4[1][3] = 0;
this.mvdX4[1][2] = 0;
this.mvdX4[1][1] = 0;
this.mvdX4[1][0] = 0;
this.mvdY1[1][3] = 0;
this.mvdY1[1][2] = 0;
this.mvdY1[1][1] = 0;
this.mvdY1[1][0] = 0;
this.mvdY2[1][3] = 0;
this.mvdY2[1][2] = 0;
this.mvdY2[1][1] = 0;
this.mvdY2[1][0] = 0;
this.mvdY3[1][3] = 0;
this.mvdY3[1][2] = 0;
this.mvdY3[1][1] = 0;
this.mvdY3[1][0] = 0;
this.mvdY4[1][3] = 0;
this.mvdY4[1][2] = 0;
this.mvdY4[1][1] = 0;
this.mvdY4[1][0] = 0;
this.subMbTypes[3] = 0;
this.subMbTypes[2] = 0;
this.subMbTypes[1] = 0;
this.subMbTypes[0] = 0;
this.refIdx[0][3] = 0;
this.refIdx[0][2] = 0;
this.refIdx[0][1] = 0;
this.refIdx[0][0] = 0;
this.refIdx[1][3] = 0;
this.refIdx[1][2] = 0;
this.refIdx[1][1] = 0;
this.refIdx[1][0] = 0;
}
}
static class IPCM {
public int[] samplesLuma = new int[256];
public int[] samplesChroma;
public IPCM(ColorSpace chromaFormat) {
int MbWidthC = 16 >> chromaFormat.compWidth[1];
int MbHeightC = 16 >> chromaFormat.compHeight[1];
this.samplesChroma = new int[2 * MbWidthC * MbHeightC];
}
public void clean() {
Arrays.fill(this.samplesLuma, 0);
Arrays.fill(this.samplesChroma, 0);
}
}
public void clear() {
this.chromaPredictionMode = 0;
this.mbQPDelta = 0;
Arrays.fill(this.dc, 0);
for (int i = 0; i < this.ac.length; i++) {
int[][] aci = this.ac[i];
for (int j = 0; j < aci.length; j++)
Arrays.fill(aci[j], 0);
}
this.transform8x8Used = false;
Arrays.fill(this.lumaModes, 0);
Arrays.fill(this.dc1, 0);
Arrays.fill(this.dc2, 0);
Arrays.fill(this.nCoeff, 0);
this._cbp = 0;
this.mbType = 0;
this.pb16x16.clean();
this.pb168x168.clean();
this.pb8x8.clean();
if (this.curMbType == MBType.I_PCM)
this.ipcm.clean();
this.mbIdx = 0;
this.fieldDecoding = false;
this.prevMbType = null;
this.luma16x16Mode = 0;
this.skipped = false;
this.curMbType = null;
this.x.clear();
this.partPreds[0] = this.partPreds[1] = this.partPreds[2] = this.partPreds[3] = null;
}
}

View file

@ -0,0 +1,256 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class MBlockDecoderBDirect extends MBlockDecoderBase {
private Mapper mapper;
public MBlockDecoderBDirect(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
}
public void decode(MBlock mBlock, Picture mb, Frame[][] references) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean lAvb = this.mapper.leftAvailable(mBlock.mbIdx);
boolean tAvb = this.mapper.topAvailable(mBlock.mbIdx);
int mbAddr = this.mapper.getAddress(mBlock.mbIdx);
boolean tlAvb = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean trAvb = this.mapper.topRightAvailable(mBlock.mbIdx);
predictBDirect(references, mbX, mbY, lAvb, tAvb, tlAvb, trAvb, mBlock.x, mBlock.partPreds, mb, H264Const.identityMapping4);
predictChromaInter(references, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(references, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][mbAddr] = this.s.qp;
residualLuma(mBlock, lAvb, tAvb, mbX, mbY);
MBlockDecoderUtils.savePrediction8x8(this.s, mbX, mBlock.x);
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
int qp1 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[0]);
int qp2 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[1]);
decodeChromaResidual(mBlock, lAvb, tAvb, mbX, mbY, qp1, qp2);
this.di.mbQps[1][mbAddr] = qp1;
this.di.mbQps[2][mbAddr] = qp2;
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[mbAddr] = mBlock.curMbType;
this.di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
}
public void predictBDirect(Frame[][] refs, int mbX, int mbY, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, H264Utils.MvList x, H264Const.PartPred[] pp, Picture mb, int[] blocks) {
if (this.sh.directSpatialMvPredFlag) {
predictBSpatialDirect(refs, mbX, mbY, lAvb, tAvb, tlAvb, trAvb, x, pp, mb, blocks);
} else {
predictBTemporalDirect(refs, mbX, mbY, lAvb, tAvb, tlAvb, trAvb, x, pp, mb, blocks);
}
}
private void predictBTemporalDirect(Frame[][] refs, int mbX, int mbY, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, H264Utils.MvList x, H264Const.PartPred[] pp, Picture mb, int[] blocks8x8) {
for (int i = 0; i < blocks8x8.length; i++) {
int blk8x8 = blocks8x8[i];
int blk4x4_0 = H264Const.BLK8x8_BLOCKS[blk8x8][0];
pp[blk8x8] = H264Const.PartPred.Bi;
if (!this.sh.sps.direct8x8InferenceFlag) {
int[] js = H264Const.BLK8x8_BLOCKS[blk8x8];
for (int j = 0; j < js.length; j++) {
int blk4x4 = js[j];
predTemp4x4(refs, mbX, mbY, x, blk4x4);
int blkIndX = blk4x4 & 0x3;
int blkIndY = blk4x4 >> 2;
MBlockDecoderUtils.debugPrint("DIRECT_4x4 [%d, %d]: (%d,%d,%d), (%d,%d,%d)", blkIndY, blkIndX, x.mv0X(blk4x4),
x.mv0Y(blk4x4), x.mv0R(blk4x4), x.mv1X(blk4x4), x.mv1Y(blk4x4), x.mv1R(blk4x4));
int blkPredX = (mbX << 6) + (blkIndX << 4);
int blkPredY = (mbY << 6) + (blkIndY << 4);
this.interpolator.getBlockLuma(refs[0][x.mv0R(blk4x4)], this.mbb[0], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4], blkPredX +
x.mv0X(blk4x4), blkPredY + x.mv0Y(blk4x4), 4, 4);
this.interpolator.getBlockLuma(refs[1][0], this.mbb[1], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4], blkPredX +
x.mv1X(blk4x4), blkPredY + x.mv1Y(blk4x4), 4, 4);
}
} else {
int blk4x4Pred = H264Const.BLK_INV_MAP[blk8x8 * 5];
predTemp4x4(refs, mbX, mbY, x, blk4x4Pred);
propagatePred(x, blk8x8, blk4x4Pred);
int blkIndX = blk4x4_0 & 0x3;
int blkIndY = blk4x4_0 >> 2;
MBlockDecoderUtils.debugPrint("DIRECT_8x8 [%d, %d]: (%d,%d,%d), (%d,%d)", blkIndY, blkIndX, x.mv0X(blk4x4_0),
x.mv0Y(blk4x4_0), x.mv0R(blk4x4_0), x.mv1X(blk4x4_0), x.mv1Y(blk4x4_0), x.mv1R(blk4x4_0));
int blkPredX = (mbX << 6) + (blkIndX << 4);
int blkPredY = (mbY << 6) + (blkIndY << 4);
this.interpolator.getBlockLuma(refs[0][x.mv0R(blk4x4_0)], this.mbb[0], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], blkPredX +
x.mv0X(blk4x4_0), blkPredY + x.mv0Y(blk4x4_0), 8, 8);
this.interpolator.getBlockLuma(refs[1][0], this.mbb[1], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], blkPredX +
x.mv1X(blk4x4_0), blkPredY + x.mv1Y(blk4x4_0), 8, 8);
}
PredictionMerger.mergePrediction(this.sh, x.mv0R(blk4x4_0), x.mv1R(blk4x4_0), H264Const.PartPred.Bi, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], 16, 8, 8,
mb.getPlaneData(0), refs, this.poc);
}
}
private void predTemp4x4(Frame[][] refs, int mbX, int mbY, H264Utils.MvList x, int blk4x4) {
Frame refL0;
int refIdxL0;
int mbWidth = this.sh.sps.picWidthInMbsMinus1 + 1;
Frame picCol = refs[1][0];
int blkIndX = blk4x4 & 0x3;
int blkIndY = blk4x4 >> 2;
int blkPosX = (mbX << 2) + blkIndX;
int blkPosY = (mbY << 2) + blkIndY;
int mvCol = picCol.getMvs().getMv(blkPosX, blkPosY, 0);
if (H264Utils.Mv.mvRef(mvCol) == -1) {
mvCol = picCol.getMvs().getMv(blkPosX, blkPosY, 1);
if (H264Utils.Mv.mvRef(mvCol) == -1) {
refIdxL0 = 0;
refL0 = refs[0][0];
} else {
refL0 = picCol.getRefsUsed()[mbY * mbWidth + mbX][1][H264Utils.Mv.mvRef(mvCol)];
refIdxL0 = findPic(refs[0], refL0);
}
} else {
refL0 = picCol.getRefsUsed()[mbY * mbWidth + mbX][0][H264Utils.Mv.mvRef(mvCol)];
refIdxL0 = findPic(refs[0], refL0);
}
int td = MathUtil.clip(picCol.getPOC() - refL0.getPOC(), -128, 127);
if (!refL0.isShortTerm() || td == 0) {
x.setPair(blk4x4, H264Utils.Mv.packMv(H264Utils.Mv.mvX(mvCol), H264Utils.Mv.mvY(mvCol), refIdxL0), 0);
} else {
int tb = MathUtil.clip(this.poc - refL0.getPOC(), -128, 127);
int tx = (16384 + Math.abs(td / 2)) / td;
int dsf = MathUtil.clip(tb * tx + 32 >> 6, -1024, 1023);
x.setPair(blk4x4, H264Utils.Mv.packMv(dsf * H264Utils.Mv.mvX(mvCol) + 128 >> 8, dsf * H264Utils.Mv.mvY(mvCol) + 128 >> 8, refIdxL0),
H264Utils.Mv.packMv(x.mv0X(blk4x4) - H264Utils.Mv.mvX(mvCol), x.mv0Y(blk4x4) - H264Utils.Mv.mvY(mvCol), 0));
}
}
private int findPic(Frame[] frames, Frame refL0) {
for (int i = 0; i < frames.length; i++) {
if (frames[i] == refL0)
return i;
}
Logger.error("RefPicList0 shall contain refPicCol");
return 0;
}
private void predictBSpatialDirect(Frame[][] refs, int mbX, int mbY, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, H264Utils.MvList x, H264Const.PartPred[] pp, Picture mb, int[] blocks8x8) {
int a0 = this.s.mvLeft.getMv(0, 0), a1 = this.s.mvLeft.getMv(0, 1);
int b0 = this.s.mvTop.getMv(mbX << 2, 0), b1 = this.s.mvTop.getMv(mbX << 2, 1);
int c0 = this.s.mvTop.getMv((mbX << 2) + 4, 0), c1 = this.s.mvTop.getMv((mbX << 2) + 4, 1);
int d0 = this.s.mvTopLeft.getMv(0, 0);
int d1 = this.s.mvTopLeft.getMv(0, 1);
int refIdxL0 = calcRef(a0, b0, c0, d0, lAvb, tAvb, tlAvb, trAvb, mbX);
int refIdxL1 = calcRef(a1, b1, c1, d1, lAvb, tAvb, tlAvb, trAvb, mbX);
if (refIdxL0 < 0 && refIdxL1 < 0) {
for (int j = 0; j < blocks8x8.length; j++) {
int blk8x8 = blocks8x8[j];
int[] js = H264Const.BLK8x8_BLOCKS[blk8x8];
for (int k = 0; k < js.length; k++) {
int blk4x4 = js[k];
x.setPair(blk4x4, 0, 0);
}
pp[blk8x8] = H264Const.PartPred.Bi;
int blkOffX = (blk8x8 & 0x1) << 5;
int blkOffY = blk8x8 >> 1 << 5;
this.interpolator.getBlockLuma(refs[0][0], this.mbb[0], H264Const.BLK_8x8_MB_OFF_LUMA[blk8x8], (mbX << 6) + blkOffX, (mbY << 6) + blkOffY, 8, 8);
this.interpolator.getBlockLuma(refs[1][0], this.mbb[1], H264Const.BLK_8x8_MB_OFF_LUMA[blk8x8], (mbX << 6) + blkOffX, (mbY << 6) + blkOffY, 8, 8);
PredictionMerger.mergePrediction(this.sh, 0, 0, H264Const.PartPred.Bi, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), H264Const.BLK_8x8_MB_OFF_LUMA[blk8x8], 16, 8, 8,
mb.getPlaneData(0), refs, this.poc);
MBlockDecoderUtils.debugPrint("DIRECT_8x8 [%d, %d]: (0,0,0), (0,0,0)", Integer.valueOf(blk8x8 & 0x2), Integer.valueOf(blk8x8 << 1 & 0x2));
}
return;
}
int mvX0 = MBlockDecoderUtils.calcMVPredictionMedian(a0, b0, c0, d0, lAvb, tAvb, trAvb, tlAvb, refIdxL0, 0);
int mvY0 = MBlockDecoderUtils.calcMVPredictionMedian(a0, b0, c0, d0, lAvb, tAvb, trAvb, tlAvb, refIdxL0, 1);
int mvX1 = MBlockDecoderUtils.calcMVPredictionMedian(a1, b1, c1, d1, lAvb, tAvb, trAvb, tlAvb, refIdxL1, 0);
int mvY1 = MBlockDecoderUtils.calcMVPredictionMedian(a1, b1, c1, d1, lAvb, tAvb, trAvb, tlAvb, refIdxL1, 1);
Frame col = refs[1][0];
H264Const.PartPred partPred = (refIdxL0 >= 0 && refIdxL1 >= 0) ? H264Const.PartPred.Bi : ((refIdxL0 >= 0) ? H264Const.PartPred.L0 : H264Const.PartPred.L1);
for (int i = 0; i < blocks8x8.length; i++) {
int blk8x8 = blocks8x8[i];
int blk4x4_0 = H264Const.BLK8x8_BLOCKS[blk8x8][0];
if (!this.sh.sps.direct8x8InferenceFlag) {
int[] js = H264Const.BLK8x8_BLOCKS[blk8x8];
for (int j = 0; j < js.length; j++) {
int blk4x4 = js[j];
pred4x4(mbX, mbY, x, pp, refIdxL0, refIdxL1, mvX0, mvY0, mvX1, mvY1, col, partPred, blk4x4);
int blkIndX = blk4x4 & 0x3;
int blkIndY = blk4x4 >> 2;
MBlockDecoderUtils.debugPrint("DIRECT_4x4 [%d, %d]: (%d,%d,%d), (%d,%d," + refIdxL1 + ")", blkIndY, blkIndX,
x.mv0X(blk4x4), x.mv0Y(blk4x4), refIdxL0, x.mv1X(blk4x4), x.mv1Y(blk4x4));
int blkPredX = (mbX << 6) + (blkIndX << 4);
int blkPredY = (mbY << 6) + (blkIndY << 4);
if (refIdxL0 >= 0)
this.interpolator.getBlockLuma(refs[0][refIdxL0], this.mbb[0], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4], blkPredX +
x.mv0X(blk4x4), blkPredY + x.mv0Y(blk4x4), 4, 4);
if (refIdxL1 >= 0)
this.interpolator.getBlockLuma(refs[1][refIdxL1], this.mbb[1], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4], blkPredX +
x.mv1X(blk4x4), blkPredY + x.mv1Y(blk4x4), 4, 4);
}
} else {
int blk4x4Pred = H264Const.BLK_INV_MAP[blk8x8 * 5];
pred4x4(mbX, mbY, x, pp, refIdxL0, refIdxL1, mvX0, mvY0, mvX1, mvY1, col, partPred, blk4x4Pred);
propagatePred(x, blk8x8, blk4x4Pred);
int blkIndX = blk4x4_0 & 0x3;
int blkIndY = blk4x4_0 >> 2;
MBlockDecoderUtils.debugPrint("DIRECT_8x8 [%d, %d]: (%d,%d,%d), (%d,%d,%d)", blkIndY, blkIndX, x.mv0X(blk4x4_0),
x.mv0Y(blk4x4_0), refIdxL0, x.mv1X(blk4x4_0), x.mv1Y(blk4x4_0), refIdxL1);
int blkPredX = (mbX << 6) + (blkIndX << 4);
int blkPredY = (mbY << 6) + (blkIndY << 4);
if (refIdxL0 >= 0)
this.interpolator.getBlockLuma(refs[0][refIdxL0], this.mbb[0], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], blkPredX +
x.mv0X(blk4x4_0), blkPredY + x.mv0Y(blk4x4_0), 8, 8);
if (refIdxL1 >= 0)
this.interpolator.getBlockLuma(refs[1][refIdxL1], this.mbb[1], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], blkPredX +
x.mv1X(blk4x4_0), blkPredY + x.mv1Y(blk4x4_0), 8, 8);
}
PredictionMerger.mergePrediction(this.sh, x.mv0R(blk4x4_0), x.mv1R(blk4x4_0),
(refIdxL0 >= 0) ? ((refIdxL1 >= 0) ? H264Const.PartPred.Bi : H264Const.PartPred.L0) : H264Const.PartPred.L1, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], 16, 8, 8,
mb.getPlaneData(0), refs, this.poc);
}
}
private int calcRef(int a0, int b0, int c0, int d0, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, int mbX) {
return minPos(minPos(lAvb ? H264Utils.Mv.mvRef(a0) : -1, tAvb ? H264Utils.Mv.mvRef(b0) : -1), trAvb ? H264Utils.Mv.mvRef(c0) : (tlAvb ? H264Utils.Mv.mvRef(d0) : -1));
}
private void propagatePred(H264Utils.MvList x, int blk8x8, int blk4x4Pred) {
int b0 = H264Const.BLK8x8_BLOCKS[blk8x8][0];
int b1 = H264Const.BLK8x8_BLOCKS[blk8x8][1];
int b2 = H264Const.BLK8x8_BLOCKS[blk8x8][2];
int b3 = H264Const.BLK8x8_BLOCKS[blk8x8][3];
x.copyPair(b0, x, blk4x4Pred);
x.copyPair(b1, x, blk4x4Pred);
x.copyPair(b2, x, blk4x4Pred);
x.copyPair(b3, x, blk4x4Pred);
}
private void pred4x4(int mbX, int mbY, H264Utils.MvList x, H264Const.PartPred[] pp, int refL0, int refL1, int mvX0, int mvY0, int mvX1, int mvY1, Frame col, H264Const.PartPred partPred, int blk4x4) {
int blkIndX = blk4x4 & 0x3;
int blkIndY = blk4x4 >> 2;
int blkPosX = (mbX << 2) + blkIndX;
int blkPosY = (mbY << 2) + blkIndY;
int mvCol = col.getMvs().getMv(blkPosX, blkPosY, 0);
if (H264Utils.Mv.mvRef(mvCol) == -1)
mvCol = col.getMvs().getMv(blkPosX, blkPosY, 1);
boolean colZero = (col.isShortTerm() && H264Utils.Mv.mvRef(mvCol) == 0 && MathUtil.abs(H264Utils.Mv.mvX(mvCol)) >> 1 == 0 &&
MathUtil.abs(H264Utils.Mv.mvY(mvCol)) >> 1 == 0);
int x0 = H264Utils.Mv.packMv(0, 0, refL0), x1 = H264Utils.Mv.packMv(0, 0, refL1);
if (refL0 > 0 || !colZero)
x0 = H264Utils.Mv.packMv(mvX0, mvY0, refL0);
if (refL1 > 0 || !colZero)
x1 = H264Utils.Mv.packMv(mvX1, mvY1, refL1);
x.setPair(blk4x4, x0, x1);
pp[H264Const.BLK_8x8_IND[blk4x4]] = partPred;
}
private int minPos(int a, int b) {
return (a >= 0 && b >= 0) ? Math.min(a, b) : Math.max(a, b);
}
}

View file

@ -0,0 +1,186 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
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.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class MBlockDecoderBase {
protected DecoderState s;
protected SliceHeader sh;
protected DeblockerInput di;
protected int poc;
protected BlockInterpolator interpolator;
protected Picture[] mbb;
protected int[][] scalingMatrix;
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 Picture[] { Picture.create(16, 16, this.s.chromaFormat), Picture.create(16, 16, this.s.chromaFormat) };
this.scalingMatrix = initScalingMatrix(sh);
}
void residualLuma(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY) {
if (!mBlock.transform8x8Used) {
residualLuma4x4(mBlock);
} else if (this.sh.pps.entropyCodingModeFlag) {
residualLuma8x8CABAC(mBlock);
} else {
residualLuma8x8CAVLC(mBlock);
}
}
private void residualLuma4x4(MBlock mBlock) {
for (int i = 0; i < 16; i++) {
if ((mBlock.cbpLuma() & 1 << i >> 2) != 0) {
CoeffTransformer.dequantizeAC(mBlock.ac[0][i], this.s.qp, getScalingList(mBlock.curMbType.intra ? 0 : 3));
CoeffTransformer.idct4x4(mBlock.ac[0][i]);
}
}
}
protected int[] getScalingList(int which) {
if (this.scalingMatrix == null)
return null;
return this.scalingMatrix[which];
}
protected static int[][] initScalingMatrix(SliceHeader sh2) {
if (sh2.sps.scalingMatrix == null && (sh2.pps.extended == null || sh2.pps.extended.scalingMatrix == null))
return null;
int[][] merged = {
H264Const.defaultScalingList4x4Intra, null, null, H264Const.defaultScalingList4x4Inter, null, null, H264Const.defaultScalingList8x8Intra, H264Const.defaultScalingList8x8Inter, null, null,
null, null };
for (int i = 0; i < 8; i++) {
if (sh2.sps.scalingMatrix != null && sh2.sps.scalingMatrix[i] != null)
merged[i] = sh2.sps.scalingMatrix[i];
if (sh2.pps.extended != null && sh2.pps.extended.scalingMatrix != null && sh2.pps.extended.scalingMatrix[i] != null)
merged[i] = sh2.pps.extended.scalingMatrix[i];
}
if (merged[1] == null)
merged[1] = merged[0];
if (merged[2] == null)
merged[2] = merged[0];
if (merged[4] == null)
merged[4] = merged[3];
if (merged[5] == null)
merged[5] = merged[3];
if (merged[8] == null)
merged[8] = merged[6];
if (merged[10] == null)
merged[10] = merged[6];
if (merged[9] == null)
merged[9] = merged[7];
if (merged[11] == null)
merged[11] = merged[7];
return merged;
}
private void residualLuma8x8CABAC(MBlock mBlock) {
for (int i = 0; i < 4; i++) {
if ((mBlock.cbpLuma() & 1 << i) != 0) {
CoeffTransformer.dequantizeAC8x8(mBlock.ac[0][i], this.s.qp, getScalingList(mBlock.curMbType.intra ? 6 : 7));
CoeffTransformer.idct8x8(mBlock.ac[0][i]);
}
}
}
private void residualLuma8x8CAVLC(MBlock mBlock) {
for (int i = 0; i < 4; i++) {
if ((mBlock.cbpLuma() & 1 << i) != 0) {
CoeffTransformer.dequantizeAC8x8(mBlock.ac[0][i], this.s.qp, getScalingList(mBlock.curMbType.intra ? 6 : 7));
CoeffTransformer.idct8x8(mBlock.ac[0][i]);
}
}
}
public void decodeChroma(MBlock mBlock, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, Picture mb, int qp) {
if (this.s.chromaFormat == ColorSpace.MONO) {
Arrays.fill(mb.getPlaneData(1), (byte)0);
Arrays.fill(mb.getPlaneData(2), (byte)0);
return;
}
int qp1 = calcQpChroma(qp, this.s.chromaQpOffset[0]);
int qp2 = calcQpChroma(qp, this.s.chromaQpOffset[1]);
if (mBlock.cbpChroma() != 0)
decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, qp1, qp2);
int addr = mbY * (this.sh.sps.picWidthInMbsMinus1 + 1) + mbX;
this.di.mbQps[1][addr] = qp1;
this.di.mbQps[2][addr] = qp2;
ChromaPredictionBuilder.predictWithMode(mBlock.ac[1], mBlock.chromaPredictionMode, mbX, leftAvailable, topAvailable, this.s.leftRow[1], this.s.topLine[1], this.s.topLeft[1],
mb.getPlaneData(1));
ChromaPredictionBuilder.predictWithMode(mBlock.ac[2], mBlock.chromaPredictionMode, mbX, leftAvailable, topAvailable, this.s.leftRow[2], this.s.topLine[2], this.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() & 0x3) > 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() & 0x2) > 0), mBlock.ac[1]);
chromaAC(leftAvailable, topAvailable, mbX, mbY, mBlock.dc2, 2, crQp2, mBlock.curMbType,
((mBlock.cbpChroma() & 0x2) > 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, getScalingList((curMbType.intra ? 6 : 7) + comp * 2));
}
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, getScalingList((curMbType.intra ? 0 : 3) + comp));
ac[0] = dc[i];
CoeffTransformer.idct4x4(ac);
}
}
static int calcQpChroma(int qp, int crQpOffset) {
return H264Const.QP_SCALE_CR[MathUtil.clip(qp + crQpOffset, 0, 51)];
}
public void predictChromaInter(Frame[][] refs, H264Utils.MvList vectors, int x, int y, int comp, Picture mb, H264Const.PartPred[] predType) {
for (int blk8x8 = 0; blk8x8 < 4; blk8x8++) {
for (int list = 0; list < 2; list++) {
if (H264Const.usesList(predType[blk8x8], list))
for (int i = 0; i < 4; i++) {
int j = H264Const.BLK_INV_MAP[(blk8x8 << 2) + i];
int mv = vectors.getMv(j, list);
Picture ref = refs[list][H264Utils.Mv.mvRef(mv)];
int blkPox = (j & 0x3) << 1;
int blkPoy = j >> 2 << 1;
int xx = (x + blkPox << 3) + H264Utils.Mv.mvX(mv);
int yy = (y + blkPoy << 3) + H264Utils.Mv.mvY(mv);
BlockInterpolator.getBlockChroma(ref.getPlaneData(comp), ref.getPlaneWidth(comp),
ref.getPlaneHeight(comp), this.mbb[list].getPlaneData(comp), blkPoy * mb.getPlaneWidth(comp) + blkPox,
mb.getPlaneWidth(comp), xx, yy, 2, 2);
}
}
int blk4x4 = H264Const.BLK8x8_BLOCKS[blk8x8][0];
PredictionMerger.mergePrediction(this.sh, vectors.mv0R(blk4x4), vectors.mv1R(blk4x4), predType[blk8x8], comp, this.mbb[0]
.getPlaneData(comp), this.mbb[1].getPlaneData(comp), H264Const.BLK_8x8_MB_OFF_CHROMA[blk8x8],
mb.getPlaneWidth(comp), 4, 4, mb.getPlaneData(comp), refs, this.poc);
}
}
}

View file

@ -0,0 +1,21 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.common.model.Picture;
public class MBlockDecoderIPCM {
private Mapper mapper;
private DecoderState s;
public MBlockDecoderIPCM(Mapper mapper, DecoderState decoderState) {
this.mapper = mapper;
this.s = decoderState;
}
public void decode(MBlock mBlock, Picture mb) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
MBlockDecoderUtils.saveVectIntra(this.s, this.mapper.getMbX(mBlock.mbIdx));
}
}

View file

@ -0,0 +1,233 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
public class MBlockDecoderInter extends MBlockDecoderBase {
private Mapper mapper;
public MBlockDecoderInter(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
}
public void decode16x16(MBlock mBlock, Picture mb, Frame[][] refs, H264Const.PartPred p0) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
int address = this.mapper.getAddress(mBlock.mbIdx);
int xx = mbX << 2;
for (int list = 0; list < 2; list++)
predictInter16x16(mBlock, this.mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, mBlock.x, xx, list, p0);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(0), mBlock.x.mv1R(0), p0, 0, this.mbb[0].getPlaneData(0), this.mbb[1]
.getPlaneData(0), 0, 16, 16, 16, mb.getPlaneData(0), refs, this.poc);
mBlock.partPreds[0] = mBlock.partPreds[1] = mBlock.partPreds[2] = mBlock.partPreds[3] = p0;
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, this.mapper.getAddress(mBlock.mbIdx));
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[address] = mBlock.curMbType;
}
private void predictInter8x16(MBlock mBlock, Picture mb, Picture[][] references, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, H264Utils.MvList x, int list, H264Const.PartPred p0, H264Const.PartPred p1) {
int xx = mbX << 2;
int mvX1 = 0, mvY1 = 0, r1 = -1, mvX2 = 0, mvY2 = 0, r2 = -1;
if (H264Const.usesList(p0, list)) {
int mvpX1 = calcMVPrediction8x16Left(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 2, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, topAvailable, tlAvailable, mBlock.pb168x168.refIdx1[list], 0);
int mvpY1 = calcMVPrediction8x16Left(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 2, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, topAvailable, tlAvailable, mBlock.pb168x168.refIdx1[list], 1);
mvX1 = mBlock.pb168x168.mvdX1[list] + mvpX1;
mvY1 = mBlock.pb168x168.mvdY1[list] + mvpY1;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb168x168.mvdX1[list], mBlock.pb168x168.mvdY1[list],
mvX1, mvY1, mBlock.pb168x168.refIdx1[list] });
this.interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx1[list]], mb, 0, (mbX << 6) + mvX1, (mbY << 6) + mvY1, 8, 16);
r1 = mBlock.pb168x168.refIdx1[list];
}
int v1 = H264Utils.Mv.packMv(mvX1, mvY1, r1);
if (H264Const.usesList(p1, list)) {
int mvpX2 = calcMVPrediction8x16Right(v1, this.s.mvTop.getMv((mbX << 2) + 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTop.getMv((mbX << 2) + 1, list), true, topAvailable, trAvailable, topAvailable, mBlock.pb168x168.refIdx2[list], 0);
int mvpY2 = calcMVPrediction8x16Right(v1, this.s.mvTop.getMv((mbX << 2) + 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTop.getMv((mbX << 2) + 1, list), true, topAvailable, trAvailable, topAvailable, mBlock.pb168x168.refIdx2[list], 1);
mvX2 = mBlock.pb168x168.mvdX2[list] + mvpX2;
mvY2 = mBlock.pb168x168.mvdY2[list] + mvpY2;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (" + mvpX2 + ", " + mvpY2 + "), MVD: (" + mBlock.pb168x168.mvdX2[list] + ", " + mBlock.pb168x168.mvdY2[list] + "), MV: (" + mvX2 + "," + mvY2 + "," + mBlock.pb168x168.refIdx2[list] + ")" });
this.interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx2[list]], mb, 8, (mbX << 6) + 32 + mvX2, (mbY << 6) + mvY2, 8, 16);
r2 = mBlock.pb168x168.refIdx2[list];
}
int v2 = H264Utils.Mv.packMv(mvX2, mvY2, r2);
this.s.mvTopLeft.setMv(0, list, this.s.mvTop.getMv(xx + 3, list));
MBlockDecoderUtils.saveVect(this.s.mvTop, list, xx, xx + 2, v1);
MBlockDecoderUtils.saveVect(this.s.mvTop, list, xx + 2, xx + 4, v2);
MBlockDecoderUtils.saveVect(this.s.mvLeft, list, 0, 4, v2);
for (int i = 0; i < 16; i += 4) {
x.setMv(i, list, v1);
x.setMv(i + 1, list, v1);
x.setMv(i + 2, list, v2);
x.setMv(i + 3, list, v2);
}
}
private void predictInter16x8(MBlock mBlock, Picture mb, Picture[][] references, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, int xx, H264Utils.MvList x, H264Const.PartPred p0, H264Const.PartPred p1, int list) {
int mvX1 = 0, mvY1 = 0, mvX2 = 0, mvY2 = 0, r1 = -1, r2 = -1;
if (H264Const.usesList(p0, list)) {
int mvpX1 = calcMVPrediction16x8Top(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, trAvailable, tlAvailable, mBlock.pb168x168.refIdx1[list], 0);
int mvpY1 = calcMVPrediction16x8Top(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, trAvailable, tlAvailable, mBlock.pb168x168.refIdx1[list], 1);
mvX1 = mBlock.pb168x168.mvdX1[list] + mvpX1;
mvY1 = mBlock.pb168x168.mvdY1[list] + mvpY1;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb168x168.mvdX1[list], mBlock.pb168x168.mvdY1[list],
mvX1, mvY1, mBlock.pb168x168.refIdx1[list] });
this.interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx1[list]], mb, 0, (mbX << 6) + mvX1, (mbY << 6) + mvY1, 16, 8);
r1 = mBlock.pb168x168.refIdx1[list];
}
int v1 = H264Utils.Mv.packMv(mvX1, mvY1, r1);
if (H264Const.usesList(p1, list)) {
int mvpX2 = calcMVPrediction16x8Bottom(this.s.mvLeft.getMv(2, list), v1, MBlockDecoderUtils.NULL_VECTOR, this.s.mvLeft.getMv(1, list), leftAvailable, true, false, leftAvailable, mBlock.pb168x168.refIdx2[list], 0);
int mvpY2 = calcMVPrediction16x8Bottom(this.s.mvLeft.getMv(2, list), v1, MBlockDecoderUtils.NULL_VECTOR, this.s.mvLeft.getMv(1, list), leftAvailable, true, false, leftAvailable, mBlock.pb168x168.refIdx2[list], 1);
mvX2 = mBlock.pb168x168.mvdX2[list] + mvpX2;
mvY2 = mBlock.pb168x168.mvdY2[list] + mvpY2;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb168x168.mvdX2[list], mBlock.pb168x168.mvdY2[list],
mvX2, mvY2, mBlock.pb168x168.refIdx2[list] });
this.interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx2[list]], mb, 128, (mbX << 6) + mvX2, (mbY << 6) + 32 + mvY2, 16, 8);
r2 = mBlock.pb168x168.refIdx2[list];
}
int v2 = H264Utils.Mv.packMv(mvX2, mvY2, r2);
this.s.mvTopLeft.setMv(0, list, this.s.mvTop.getMv(xx + 3, list));
MBlockDecoderUtils.saveVect(this.s.mvLeft, list, 0, 2, v1);
MBlockDecoderUtils.saveVect(this.s.mvLeft, list, 2, 4, v2);
MBlockDecoderUtils.saveVect(this.s.mvTop, list, xx, xx + 4, v2);
for (int j = 0; j < 8; j++)
x.setMv(j, list, v1);
for (int i = 8; i < 16; i++)
x.setMv(i, list, v2);
}
public void decode16x8(MBlock mBlock, Picture mb, Frame[][] refs, H264Const.PartPred p0, H264Const.PartPred p1) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
int address = this.mapper.getAddress(mBlock.mbIdx);
int xx = mbX << 2;
for (int list = 0; list < 2; list++)
predictInter16x8(mBlock, this.mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, xx, mBlock.x, p0, p1, list);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(0), mBlock.x.mv1R(0), p0, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), 0, 16, 16, 8,
mb.getPlaneData(0), refs, this.poc);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(8), mBlock.x.mv1R(8), p1, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), 128, 16, 16, 8,
mb.getPlaneData(0), refs, this.poc);
mBlock.partPreds[0] = mBlock.partPreds[1] = p0;
mBlock.partPreds[2] = mBlock.partPreds[3] = p1;
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, this.mapper.getAddress(mBlock.mbIdx));
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[address] = mBlock.curMbType;
}
public void decode8x16(MBlock mBlock, Picture mb, Frame[][] refs, H264Const.PartPred p0, H264Const.PartPred p1) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
int address = this.mapper.getAddress(mBlock.mbIdx);
for (int list = 0; list < 2; list++)
predictInter8x16(mBlock, this.mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, mBlock.x, list, p0, p1);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(0), mBlock.x.mv1R(0), p0, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), 0, 16, 8, 16,
mb.getPlaneData(0), refs, this.poc);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(2), mBlock.x.mv1R(2), p1, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), 8, 16, 8, 16,
mb.getPlaneData(0), refs, this.poc);
mBlock.partPreds[0] = mBlock.partPreds[2] = p0;
mBlock.partPreds[1] = mBlock.partPreds[3] = p1;
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, this.mapper.getAddress(mBlock.mbIdx));
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[address] = mBlock.curMbType;
}
void predictInter16x16(MBlock mBlock, Picture mb, Picture[][] references, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, H264Utils.MvList x, int xx, int list, H264Const.PartPred curPred) {
int mvX = 0, mvY = 0, r = -1;
if (H264Const.usesList(curPred, list)) {
int mvpX = MBlockDecoderUtils.calcMVPredictionMedian(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, trAvailable, tlAvailable, mBlock.pb16x16.refIdx[list], 0);
int mvpY = MBlockDecoderUtils.calcMVPredictionMedian(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, trAvailable, tlAvailable, mBlock.pb16x16.refIdx[list], 1);
mvX = mBlock.pb16x16.mvdX[list] + mvpX;
mvY = mBlock.pb16x16.mvdY[list] + mvpY;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX, mvpY, mBlock.pb16x16.mvdX[list], mBlock.pb16x16.mvdY[list],
mvX, mvY, mBlock.pb16x16.refIdx[list] });
r = mBlock.pb16x16.refIdx[list];
this.interpolator.getBlockLuma(references[list][r], mb, 0, (mbX << 6) + mvX, (mbY << 6) + mvY, 16, 16);
}
int v = H264Utils.Mv.packMv(mvX, mvY, r);
this.s.mvTopLeft.setMv(0, list, this.s.mvTop.getMv(xx + 3, list));
MBlockDecoderUtils.saveVect(this.s.mvTop, list, xx, xx + 4, v);
MBlockDecoderUtils.saveVect(this.s.mvLeft, list, 0, 4, v);
for (int i = 0; i < 16; i++)
x.setMv(i, list, v);
}
private void residualInter(MBlock mBlock, Frame[][] refs, boolean leftAvailable, boolean topAvailable, int mbX, int mbY, int mbAddr) {
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][mbAddr] = this.s.qp;
residualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
if (this.s.chromaFormat != ColorSpace.MONO) {
int qp1 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[0]);
int qp2 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[1]);
decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, qp1, qp2);
this.di.mbQps[1][mbAddr] = qp1;
this.di.mbQps[2][mbAddr] = qp2;
}
this.di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
}
public int calcMVPrediction16x8Top(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int refIdx, int comp) {
if (bAvb && H264Utils.Mv.mvRef(b) == refIdx)
return H264Utils.Mv.mvC(b, comp);
return MBlockDecoderUtils.calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction16x8Bottom(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int refIdx, int comp) {
if (aAvb && H264Utils.Mv.mvRef(a) == refIdx)
return H264Utils.Mv.mvC(a, comp);
return MBlockDecoderUtils.calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction8x16Left(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int refIdx, int comp) {
if (aAvb && H264Utils.Mv.mvRef(a) == refIdx)
return H264Utils.Mv.mvC(a, comp);
return MBlockDecoderUtils.calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction8x16Right(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int refIdx, int comp) {
int lc = cAvb ? c : (dAvb ? d : MBlockDecoderUtils.NULL_VECTOR);
if (H264Utils.Mv.mvRef(lc) == refIdx)
return H264Utils.Mv.mvC(lc, comp);
return MBlockDecoderUtils.calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
}

View file

@ -0,0 +1,211 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.model.Picture;
public class MBlockDecoderInter8x8 extends MBlockDecoderBase {
private Mapper mapper;
private MBlockDecoderBDirect bDirectDecoder;
public MBlockDecoderInter8x8(Mapper mapper, MBlockDecoderBDirect bDirectDecoder, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
this.bDirectDecoder = bDirectDecoder;
}
public void decode(MBlock mBlock, Frame[][] references, Picture mb, SliceType sliceType, boolean ref0) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
int mbAddr = this.mapper.getAddress(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
if (sliceType == SliceType.P) {
predict8x8P(mBlock, references[0], mb, ref0, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, mBlock.x, mBlock.partPreds);
} else {
predict8x8B(mBlock, references, mb, ref0, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, mBlock.x, mBlock.partPreds);
}
predictChromaInter(references, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(references, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][mbAddr] = this.s.qp;
residualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
int qp1 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[0]);
int qp2 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[1]);
decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, qp1, qp2);
this.di.mbQps[1][mbAddr] = qp1;
this.di.mbQps[2][mbAddr] = qp2;
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[mbAddr] = mBlock.curMbType;
this.di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
}
private void predict8x8P(MBlock mBlock, Picture[] references, Picture mb, boolean ref0, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean topRightAvailable, H264Utils.MvList x, H264Const.PartPred[] pp) {
decodeSubMb8x8(mBlock, 0, mBlock.pb8x8.subMbTypes[0], references, mbX << 6, mbY << 6, this.s.mvTopLeft.getMv(0, 0),
this.s.mvTop.getMv(mbX << 2, 0), this.s.mvTop.getMv((mbX << 2) + 1, 0), this.s.mvTop.getMv((mbX << 2) + 2, 0),
this.s.mvLeft.getMv(0, 0), this.s.mvLeft.getMv(1, 0), tlAvailable, topAvailable, topAvailable, leftAvailable, mBlock.x, 0, 1, 4, 5, mBlock.pb8x8.refIdx[0][0], mb, 0, 0);
decodeSubMb8x8(mBlock, 1, mBlock.pb8x8.subMbTypes[1], references, (mbX << 6) + 32, mbY << 6,
this.s.mvTop.getMv((mbX << 2) + 1, 0), this.s.mvTop.getMv((mbX << 2) + 2, 0), this.s.mvTop.getMv((mbX << 2) + 3, 0),
this.s.mvTop.getMv((mbX << 2) + 4, 0), x.getMv(1, 0), x.getMv(5, 0), topAvailable, topAvailable, topRightAvailable, true, x, 2, 3, 6, 7, mBlock.pb8x8.refIdx[0][1], mb, 8, 0);
decodeSubMb8x8(mBlock, 2, mBlock.pb8x8.subMbTypes[2], references, mbX << 6, (mbY << 6) + 32,
this.s.mvLeft.getMv(1, 0), x.getMv(4, 0), x.getMv(5, 0), x.getMv(6, 0), this.s.mvLeft.getMv(2, 0),
this.s.mvLeft.getMv(3, 0), leftAvailable, true, true, leftAvailable, x, 8, 9, 12, 13, mBlock.pb8x8.refIdx[0][2], mb, 128, 0);
decodeSubMb8x8(mBlock, 3, mBlock.pb8x8.subMbTypes[3], references, (mbX << 6) + 32, (mbY << 6) + 32,
x.getMv(5, 0), x.getMv(6, 0), x.getMv(7, 0), MBlockDecoderUtils.NULL_VECTOR, x.getMv(9, 0), x.getMv(13, 0), true, true, false, true, x, 10, 11, 14, 15, mBlock.pb8x8.refIdx[0][3], mb, 136, 0);
for (int i = 0; i < 4; i++) {
int blk4x4 = H264Const.BLK8x8_BLOCKS[i][0];
PredictionMerger.weightPrediction(this.sh, x.mv0R(blk4x4), 0, mb.getPlaneData(0), H264Const.BLK_8x8_MB_OFF_LUMA[i], 16, 8, 8,
mb.getPlaneData(0));
}
MBlockDecoderUtils.savePrediction8x8(this.s, mbX, x);
Arrays.fill(pp, H264Const.PartPred.L0);
}
private void predict8x8B(MBlock mBlock, Frame[][] refs, Picture mb, boolean ref0, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean topRightAvailable, H264Utils.MvList x, H264Const.PartPred[] p) {
for (int k = 0; k < 4; k++)
p[k] = H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[k]];
for (int i = 0; i < 4; i++) {
if (p[i] == H264Const.PartPred.Direct)
this.bDirectDecoder.predictBDirect(refs, mbX, mbY, leftAvailable, topAvailable, tlAvailable, topRightAvailable, x, p, mb, H264Const.ARRAY[i]);
}
for (int list = 0; list < 2; list++) {
if (H264Const.usesList(H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[0]], list))
decodeSubMb8x8(mBlock, 0, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[0]], refs[list], mbX << 6, mbY << 6,
this.s.mvTopLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list), this.s.mvTop.getMv((mbX << 2) + 1, list),
this.s.mvTop.getMv((mbX << 2) + 2, list), this.s.mvLeft.getMv(0, list), this.s.mvLeft.getMv(1, list), tlAvailable, topAvailable, topAvailable, leftAvailable, x, 0, 1, 4, 5, mBlock.pb8x8.refIdx[list][0], this.mbb[list], 0, list);
if (H264Const.usesList(H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[1]], list))
decodeSubMb8x8(mBlock, 1, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[1]], refs[list], (mbX << 6) + 32, mbY << 6,
this.s.mvTop.getMv((mbX << 2) + 1, list), this.s.mvTop.getMv((mbX << 2) + 2, list),
this.s.mvTop.getMv((mbX << 2) + 3, list), this.s.mvTop.getMv((mbX << 2) + 4, list), x.getMv(1, list),
x.getMv(5, list), topAvailable, topAvailable, topRightAvailable, true, x, 2, 3, 6, 7, mBlock.pb8x8.refIdx[list][1], this.mbb[list], 8, list);
if (H264Const.usesList(H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[2]], list))
decodeSubMb8x8(mBlock, 2, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[2]], refs[list], mbX << 6, (mbY << 6) + 32,
this.s.mvLeft.getMv(1, list), x.getMv(4, list), x.getMv(5, list), x.getMv(6, list),
this.s.mvLeft.getMv(2, list), this.s.mvLeft.getMv(3, list), leftAvailable, true, true, leftAvailable, x, 8, 9, 12, 13, mBlock.pb8x8.refIdx[list][2], this.mbb[list], 128, list);
if (H264Const.usesList(H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[3]], list))
decodeSubMb8x8(mBlock, 3, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[3]], refs[list], (mbX << 6) + 32, (mbY << 6) + 32,
x.getMv(5, list), x.getMv(6, list), x.getMv(7, list), MBlockDecoderUtils.NULL_VECTOR,
x.getMv(9, list), x.getMv(13, list), true, true, false, true, x, 10, 11, 14, 15, mBlock.pb8x8.refIdx[list][3], this.mbb[list], 136, list);
}
for (int j = 0; j < 4; j++) {
int blk4x4 = H264Const.BLK8x8_BLOCKS[j][0];
PredictionMerger.mergePrediction(this.sh, x.mv0R(blk4x4), x.mv1R(blk4x4), H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[j]], 0, this.mbb[0]
.getPlaneData(0), this.mbb[1].getPlaneData(0), H264Const.BLK_8x8_MB_OFF_LUMA[j], 16, 8, 8,
mb.getPlaneData(0), refs, this.poc);
}
MBlockDecoderUtils.savePrediction8x8(this.s, mbX, x);
}
private void decodeSubMb8x8(MBlock mBlock, int partNo, int subMbType, Picture[] references, int offX, int offY, int tl, int t0, int t1, int tr, int l0, int l1, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
switch (subMbType) {
case 3:
decodeSub4x4(mBlock, partNo, references, offX, offY, tl, t0, t1, tr, l0, l1, tlAvb, tAvb, trAvb, lAvb, x, i00, i01, i10, i11, refIdx, mb, off, list);
break;
case 2:
decodeSub4x8(mBlock, partNo, references, offX, offY, tl, t0, t1, tr, l0, tlAvb, tAvb, trAvb, lAvb, x, i00, i01, i10, i11, refIdx, mb, off, list);
break;
case 1:
decodeSub8x4(mBlock, partNo, references, offX, offY, tl, t0, tr, l0, l1, tlAvb, tAvb, trAvb, lAvb, x, i00, i01, i10, i11, refIdx, mb, off, list);
break;
case 0:
decodeSub8x8(mBlock, partNo, references, offX, offY, tl, t0, tr, l0, tlAvb, tAvb, trAvb, lAvb, x, i00, i01, i10, i11, refIdx, mb, off, list);
break;
}
}
private void decodeSub8x8(MBlock mBlock, int partNo, Picture[] references, int offX, int offY, int tl, int t0, int tr, int l0, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
int mvpX = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 0);
int mvpY = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 1);
int mv = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX1[list][partNo] + mvpX, mBlock.pb8x8.mvdY1[list][partNo] + mvpY, refIdx);
x.setMv(i00, list, mv);
x.setMv(i01, list, mv);
x.setMv(i10, list, mv);
x.setMv(i11, list, mv);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX, mvpY, mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo],
H264Utils.Mv.mvX(mv), H264Utils.Mv.mvY(mv), refIdx);
this.interpolator.getBlockLuma(references[refIdx], mb, off, offX + H264Utils.Mv.mvX(mv), offY + H264Utils.Mv.mvY(mv), 8, 8);
}
private void decodeSub8x4(MBlock mBlock, int partNo, Picture[] references, int offX, int offY, int tl, int t0, int tr, int l0, int l1, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
int mvpX1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 0);
int mvpY1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 1);
int mv1 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX1[list][partNo] + mvpX1, mBlock.pb8x8.mvdY1[list][partNo] + mvpY1, refIdx);
x.setMv(i00, list, mv1);
x.setMv(i01, list, mv1);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo],
H264Utils.Mv.mvX(mv1), H264Utils.Mv.mvY(mv1), refIdx);
int mvpX2 = MBlockDecoderUtils.calcMVPredictionMedian(l1, mv1, MBlockDecoderUtils.NULL_VECTOR, l0, lAvb, true, false, lAvb, refIdx, 0);
int mvpY2 = MBlockDecoderUtils.calcMVPredictionMedian(l1, mv1, MBlockDecoderUtils.NULL_VECTOR, l0, lAvb, true, false, lAvb, refIdx, 1);
int mv2 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX2[list][partNo] + mvpX2, mBlock.pb8x8.mvdY2[list][partNo] + mvpY2, refIdx);
x.setMv(i10, list, mv2);
x.setMv(i11, list, mv2);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo], mBlock.pb8x8.mvdY2[list][partNo],
H264Utils.Mv.mvX(mv2), H264Utils.Mv.mvY(mv2), refIdx);
this.interpolator.getBlockLuma(references[refIdx], mb, off, offX + H264Utils.Mv.mvX(mv1), offY + H264Utils.Mv.mvY(mv1), 8, 4);
this.interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4, offX + H264Utils.Mv.mvX(mv2), offY +
H264Utils.Mv.mvY(mv2) + 16, 8, 4);
}
private void decodeSub4x8(MBlock mBlock, int partNo, Picture[] references, int offX, int offY, int tl, int t0, int t1, int tr, int l0, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
int mvpX1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 0);
int mvpY1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 1);
int mv1 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX1[list][partNo] + mvpX1, mBlock.pb8x8.mvdY1[list][partNo] + mvpY1, refIdx);
x.setMv(i00, list, mv1);
x.setMv(i10, list, mv1);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo],
H264Utils.Mv.mvX(mv1), H264Utils.Mv.mvY(mv1), refIdx);
int mvpX2 = MBlockDecoderUtils.calcMVPredictionMedian(mv1, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 0);
int mvpY2 = MBlockDecoderUtils.calcMVPredictionMedian(mv1, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 1);
int mv2 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX2[list][partNo] + mvpX2, mBlock.pb8x8.mvdY2[list][partNo] + mvpY2, refIdx);
x.setMv(i01, list, mv2);
x.setMv(i11, list, mv2);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo], mBlock.pb8x8.mvdY2[list][partNo],
H264Utils.Mv.mvX(mv2), H264Utils.Mv.mvY(mv2), refIdx);
this.interpolator.getBlockLuma(references[refIdx], mb, off, offX + H264Utils.Mv.mvX(mv1), offY + H264Utils.Mv.mvY(mv1), 4, 8);
this.interpolator.getBlockLuma(references[refIdx], mb, off + 4, offX + H264Utils.Mv.mvX(mv2) + 16, offY + H264Utils.Mv.mvY(mv2), 4, 8);
}
private void decodeSub4x4(MBlock mBlock, int partNo, Picture[] references, int offX, int offY, int tl, int t0, int t1, int tr, int l0, int l1, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
int mvpX1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 0);
int mvpY1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 1);
int mv1 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX1[list][partNo] + mvpX1, mBlock.pb8x8.mvdY1[list][partNo] + mvpY1, refIdx);
x.setMv(i00, list, mv1);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo],
H264Utils.Mv.mvX(mv1), H264Utils.Mv.mvY(mv1), refIdx);
int mvpX2 = MBlockDecoderUtils.calcMVPredictionMedian(mv1, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 0);
int mvpY2 = MBlockDecoderUtils.calcMVPredictionMedian(mv1, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 1);
int mv2 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX2[list][partNo] + mvpX2, mBlock.pb8x8.mvdY2[list][partNo] + mvpY2, refIdx);
x.setMv(i01, list, mv2);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo], mBlock.pb8x8.mvdY2[list][partNo],
H264Utils.Mv.mvX(mv2), H264Utils.Mv.mvY(mv2), refIdx);
int mvpX3 = MBlockDecoderUtils.calcMVPredictionMedian(l1, mv1, mv2, l0, lAvb, true, true, lAvb, refIdx, 0);
int mvpY3 = MBlockDecoderUtils.calcMVPredictionMedian(l1, mv1, mv2, l0, lAvb, true, true, lAvb, refIdx, 1);
int mv3 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX3[list][partNo] + mvpX3, mBlock.pb8x8.mvdY3[list][partNo] + mvpY3, refIdx);
x.setMv(i10, list, mv3);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX3, mvpY3, mBlock.pb8x8.mvdX3[list][partNo], mBlock.pb8x8.mvdY3[list][partNo],
H264Utils.Mv.mvX(mv3), H264Utils.Mv.mvY(mv3), refIdx);
int mvpX4 = MBlockDecoderUtils.calcMVPredictionMedian(mv3, mv2, MBlockDecoderUtils.NULL_VECTOR, mv1, true, true, false, true, refIdx, 0);
int mvpY4 = MBlockDecoderUtils.calcMVPredictionMedian(mv3, mv2, MBlockDecoderUtils.NULL_VECTOR, mv1, true, true, false, true, refIdx, 1);
int mv4 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX4[list][partNo] + mvpX4, mBlock.pb8x8.mvdY4[list][partNo] + mvpY4, refIdx);
x.setMv(i11, list, mv4);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX4, mvpY4, mBlock.pb8x8.mvdX4[list][partNo], mBlock.pb8x8.mvdY4[list][partNo],
H264Utils.Mv.mvX(mv4), H264Utils.Mv.mvY(mv4), refIdx);
this.interpolator.getBlockLuma(references[refIdx], mb, off, offX + H264Utils.Mv.mvX(mv1), offY + H264Utils.Mv.mvY(mv1), 4, 4);
this.interpolator.getBlockLuma(references[refIdx], mb, off + 4, offX + H264Utils.Mv.mvX(mv2) + 16, offY + H264Utils.Mv.mvY(mv2), 4, 4);
this.interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4, offX + H264Utils.Mv.mvX(mv3), offY + H264Utils.Mv.mvY(mv3) + 16, 4, 4);
this.interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4 + 4, offX + H264Utils.Mv.mvX(mv4) + 16, offY +
H264Utils.Mv.mvY(mv4) + 16, 4, 4);
}
}

View file

@ -0,0 +1,45 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.Picture;
public class MBlockDecoderIntra16x16 extends MBlockDecoderBase {
private Mapper mapper;
public MBlockDecoderIntra16x16(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
}
public void decode(MBlock mBlock, Picture mb) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
int address = this.mapper.getAddress(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][address] = this.s.qp;
residualLumaI16x16(mBlock, leftAvailable, topAvailable, mbX, mbY);
Intra16x16PredictionBuilder.predictWithMode(mBlock.luma16x16Mode, mBlock.ac[0], leftAvailable, topAvailable, this.s.leftRow[0], this.s.topLine[0], this.s.topLeft[0], mbX << 4,
mb.getPlaneData(0));
decodeChroma(mBlock, mbX, mbY, leftAvailable, topAvailable, mb, this.s.qp);
this.di.mbTypes[address] = mBlock.curMbType;
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
MBlockDecoderUtils.saveMvsIntra(this.di, mbX, mbY);
MBlockDecoderUtils.saveVectIntra(this.s, this.mapper.getMbX(mBlock.mbIdx));
}
private void residualLumaI16x16(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY) {
CoeffTransformer.invDC4x4(mBlock.dc);
int[] scalingList = getScalingList(0);
CoeffTransformer.dequantizeDC4x4(mBlock.dc, this.s.qp, scalingList);
CoeffTransformer.reorderDC4x4(mBlock.dc);
for (int i = 0; i < 16; i++) {
if ((mBlock.cbpLuma() & 1 << i >> 2) != 0)
CoeffTransformer.dequantizeAC(mBlock.ac[0][i], this.s.qp, scalingList);
mBlock.ac[0][i][0] = mBlock.dc[i];
CoeffTransformer.idct4x4(mBlock.ac[0][i]);
}
}
}

View file

@ -0,0 +1,60 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.Picture;
public class MBlockDecoderIntraNxN extends MBlockDecoderBase {
private Mapper mapper;
private Intra8x8PredictionBuilder prediction8x8Builder;
public MBlockDecoderIntraNxN(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
this.prediction8x8Builder = new Intra8x8PredictionBuilder();
}
public void decode(MBlock mBlock, Picture mb) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
int mbAddr = this.mapper.getAddress(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][mbAddr] = this.s.qp;
residualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
if (!mBlock.transform8x8Used) {
for (int i = 0; i < 16; i++) {
int blkX = (i & 0x3) << 2;
int blkY = i & 0xFFFFFFFC;
int bi = H264Const.BLK_INV_MAP[i];
boolean trAvailable = (((bi == 0 || bi == 1 || bi == 4) && topAvailable) || (bi == 5 && topRightAvailable) || bi == 2 || bi == 6 || bi == 8 || bi == 9 || bi == 10 || bi == 12 || bi == 14);
Intra4x4PredictionBuilder.predictWithMode(mBlock.lumaModes[bi], mBlock.ac[0][bi],
(blkX == 0) ? leftAvailable : true, (blkY == 0) ? topAvailable : true, trAvailable, this.s.leftRow[0], this.s.topLine[0], this.s.topLeft[0], mbX << 4, blkX, blkY,
mb.getPlaneData(0));
}
} else {
for (int i = 0; i < 4; i++) {
int blkX = (i & 0x1) << 1;
int blkY = i & 0x2;
boolean trAvailable = ((i == 0 && topAvailable) || (i == 1 && topRightAvailable) || i == 2);
boolean tlAvailable = (i == 0) ? topLeftAvailable : ((i == 1) ? topAvailable : ((i == 2) ? leftAvailable :
true));
this.prediction8x8Builder.predictWithMode(mBlock.lumaModes[i], mBlock.ac[0][i],
(blkX == 0) ? leftAvailable : true, (blkY == 0) ? topAvailable : true, tlAvailable, trAvailable, this.s.leftRow[0], this.s.topLine[0], this.s.topLeft[0], mbX << 4, blkX << 2, blkY << 2,
mb.getPlaneData(0));
}
}
decodeChroma(mBlock, mbX, mbY, leftAvailable, topAvailable, mb, this.s.qp);
this.di.mbTypes[mbAddr] = mBlock.curMbType;
this.di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
MBlockDecoderUtils.collectChromaPredictors(this.s, mb, mbX);
MBlockDecoderUtils.saveMvsIntra(this.di, mbX, mbY);
MBlockDecoderUtils.saveVectIntra(this.s, this.mapper.getMbX(mBlock.mbIdx));
}
}

View file

@ -0,0 +1,130 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.common.ArrayUtil;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class MBlockDecoderUtils {
private static boolean debug;
public static final int NULL_VECTOR = H264Utils.Mv.packMv(0, 0, -1);
public static void debugPrint(Object... arguments) {
if (debug && arguments.length > 0)
if (arguments.length == 1) {
Logger.debug(String.valueOf(arguments[0]));
} else {
String fmt = (String)arguments[0];
ArrayUtil.shiftLeft1(arguments);
Logger.debug(String.format(fmt, arguments));
}
}
static void collectPredictors(DecoderState sharedState, Picture outMB, int mbX) {
sharedState.topLeft[0][0] = sharedState.topLine[0][(mbX << 4) + 15];
sharedState.topLeft[0][1] = outMB.getPlaneData(0)[63];
sharedState.topLeft[0][2] = outMB.getPlaneData(0)[127];
sharedState.topLeft[0][3] = outMB.getPlaneData(0)[191];
System.arraycopy(outMB.getPlaneData(0), 240, sharedState.topLine[0], mbX << 4, 16);
copyCol(outMB.getPlaneData(0), 16, 15, 16, sharedState.leftRow[0]);
collectChromaPredictors(sharedState, outMB, mbX);
}
static void collectChromaPredictors(DecoderState sharedState, Picture outMB, int mbX) {
sharedState.topLeft[1][0] = sharedState.topLine[1][(mbX << 3) + 7];
sharedState.topLeft[2][0] = sharedState.topLine[2][(mbX << 3) + 7];
System.arraycopy(outMB.getPlaneData(1), 56, sharedState.topLine[1], mbX << 3, 8);
System.arraycopy(outMB.getPlaneData(2), 56, sharedState.topLine[2], mbX << 3, 8);
copyCol(outMB.getPlaneData(1), 8, 7, 8, sharedState.leftRow[1]);
copyCol(outMB.getPlaneData(2), 8, 7, 8, sharedState.leftRow[2]);
}
private static void copyCol(byte[] planeData, int n, int off, int stride, byte[] out) {
for (int i = 0; i < n; i++, off += stride)
out[i] = planeData[off];
}
static void saveMvsIntra(DeblockerInput di, int mbX, int mbY) {
for (int j = 0, blkOffY = mbY << 2, blkInd = 0; j < 4; j++, blkOffY++) {
for (int i = 0, blkOffX = mbX << 2; i < 4; i++, blkOffX++, blkInd++) {
di.mvs.setMv(blkOffX, blkOffY, 0, NULL_VECTOR);
di.mvs.setMv(blkOffX, blkOffY, 1, NULL_VECTOR);
}
}
}
static void mergeResidual(Picture mb, int[][][] residual, int[][] blockLUT, int[][] posLUT) {
for (int comp = 0; comp < 3; comp++) {
byte[] to = mb.getPlaneData(comp);
for (int i = 0; i < to.length; i++)
to[i] = (byte)MathUtil.clip(to[i] + residual[comp][blockLUT[comp][i]][posLUT[comp][i]], -128, 127);
}
}
static void saveVect(H264Utils.MvList mv, int list, int from, int to, int vect) {
for (int i = from; i < to; i++)
mv.setMv(i, list, vect);
}
public static int calcMVPredictionMedian(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int ref, int comp) {
if (!cAvb) {
c = d;
cAvb = dAvb;
}
if (aAvb && !bAvb && !cAvb) {
b = c = a;
bAvb = cAvb = aAvb;
}
a = aAvb ? a : NULL_VECTOR;
b = bAvb ? b : NULL_VECTOR;
c = cAvb ? c : NULL_VECTOR;
if (H264Utils.Mv.mvRef(a) == ref && H264Utils.Mv.mvRef(b) != ref && H264Utils.Mv.mvRef(c) != ref)
return H264Utils.Mv.mvC(a, comp);
if (H264Utils.Mv.mvRef(b) == ref && H264Utils.Mv.mvRef(a) != ref && H264Utils.Mv.mvRef(c) != ref)
return H264Utils.Mv.mvC(b, comp);
if (H264Utils.Mv.mvRef(c) == ref && H264Utils.Mv.mvRef(a) != ref && H264Utils.Mv.mvRef(b) != ref)
return H264Utils.Mv.mvC(c, comp);
return H264Utils.Mv.mvC(a, comp) + H264Utils.Mv.mvC(b, comp) + H264Utils.Mv.mvC(c, comp) - min(H264Utils.Mv.mvC(a, comp), H264Utils.Mv.mvC(b, comp), H264Utils.Mv.mvC(c, comp)) -
max(H264Utils.Mv.mvC(a, comp), H264Utils.Mv.mvC(b, comp), H264Utils.Mv.mvC(c, comp));
}
public static int max(int x, int x2, int x3) {
return (x > x2) ? ((x > x3) ? x : x3) : ((x2 > x3) ? x2 : x3);
}
public static int min(int x, int x2, int x3) {
return (x < x2) ? ((x < x3) ? x : x3) : ((x2 < x3) ? x2 : x3);
}
static void saveMvs(DeblockerInput di, H264Utils.MvList x, int mbX, int mbY) {
for (int j = 0, blkOffY = mbY << 2, blkInd = 0; j < 4; j++, blkOffY++) {
for (int i = 0, blkOffX = mbX << 2; i < 4; i++, blkOffX++, blkInd++) {
di.mvs.setMv(blkOffX, blkOffY, 0, x.getMv(blkInd, 0));
di.mvs.setMv(blkOffX, blkOffY, 1, x.getMv(blkInd, 1));
}
}
}
static void savePrediction8x8(DecoderState sharedState, int mbX, H264Utils.MvList x) {
sharedState.mvTopLeft.copyPair(0, sharedState.mvTop, (mbX << 2) + 3);
sharedState.mvLeft.copyPair(0, x, 3);
sharedState.mvLeft.copyPair(1, x, 7);
sharedState.mvLeft.copyPair(2, x, 11);
sharedState.mvLeft.copyPair(3, x, 15);
sharedState.mvTop.copyPair(mbX << 2, x, 12);
sharedState.mvTop.copyPair((mbX << 2) + 1, x, 13);
sharedState.mvTop.copyPair((mbX << 2) + 2, x, 14);
sharedState.mvTop.copyPair((mbX << 2) + 3, x, 15);
}
public static void saveVectIntra(DecoderState sharedState, int mbX) {
int xx = mbX << 2;
sharedState.mvTopLeft.copyPair(0, sharedState.mvTop, xx + 3);
saveVect(sharedState.mvTop, 0, xx, xx + 4, NULL_VECTOR);
saveVect(sharedState.mvLeft, 0, 0, 4, NULL_VECTOR);
saveVect(sharedState.mvTop, 1, xx, xx + 4, NULL_VECTOR);
saveVect(sharedState.mvLeft, 1, 0, 4, NULL_VECTOR);
}
}

View file

@ -0,0 +1,3 @@
package org.jcodec.codecs.h264.decode;
public class MBlockReader {}

View file

@ -0,0 +1,72 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.model.Picture;
public class MBlockSkipDecoder extends MBlockDecoderBase {
private Mapper mapper;
private MBlockDecoderBDirect bDirectDecoder;
public MBlockSkipDecoder(Mapper mapper, MBlockDecoderBDirect bDirectDecoder, SliceHeader sh, DeblockerInput di, int poc, DecoderState sharedState) {
super(sh, di, poc, sharedState);
this.mapper = mapper;
this.bDirectDecoder = bDirectDecoder;
}
public void decodeSkip(MBlock mBlock, Frame[][] refs, Picture mb, SliceType sliceType) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
int mbAddr = this.mapper.getAddress(mBlock.mbIdx);
if (sliceType == SliceType.P) {
predictPSkip(refs, mbX, mbY, this.mapper.leftAvailable(mBlock.mbIdx), this.mapper.topAvailable(mBlock.mbIdx),
this.mapper.topLeftAvailable(mBlock.mbIdx), this.mapper.topRightAvailable(mBlock.mbIdx), mBlock.x, mb);
Arrays.fill(mBlock.partPreds, H264Const.PartPred.L0);
} else {
this.bDirectDecoder.predictBDirect(refs, mbX, mbY, this.mapper.leftAvailable(mBlock.mbIdx),
this.mapper.topAvailable(mBlock.mbIdx), this.mapper.topLeftAvailable(mBlock.mbIdx),
this.mapper.topRightAvailable(mBlock.mbIdx), mBlock.x, mBlock.partPreds, mb, H264Const.identityMapping4);
MBlockDecoderUtils.savePrediction8x8(this.s, mbX, mBlock.x);
}
decodeChromaSkip(refs, mBlock.x, mBlock.partPreds, mbX, mbY, mb);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
this.di.mbTypes[mbAddr] = mBlock.curMbType;
this.di.mbQps[0][mbAddr] = this.s.qp;
this.di.mbQps[1][mbAddr] = calcQpChroma(this.s.qp, this.s.chromaQpOffset[0]);
this.di.mbQps[2][mbAddr] = calcQpChroma(this.s.qp, this.s.chromaQpOffset[1]);
}
public void predictPSkip(Frame[][] refs, int mbX, int mbY, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, H264Utils.MvList x, Picture mb) {
int mvX = 0, mvY = 0;
if (lAvb && tAvb) {
int b = this.s.mvTop.getMv(mbX << 2, 0);
int a = this.s.mvLeft.getMv(0, 0);
if (a != 0 && b != 0) {
mvX = MBlockDecoderUtils.calcMVPredictionMedian(a, b, this.s.mvTop.getMv((mbX << 2) + 4, 0), this.s.mvTopLeft.getMv(0, 0), lAvb, tAvb, trAvb, tlAvb, 0, 0);
mvY = MBlockDecoderUtils.calcMVPredictionMedian(a, b, this.s.mvTop.getMv((mbX << 2) + 4, 0), this.s.mvTopLeft.getMv(0, 0), lAvb, tAvb, trAvb, tlAvb, 0, 1);
}
}
int xx = mbX << 2;
this.s.mvTopLeft.copyPair(0, this.s.mvTop, xx + 3);
MBlockDecoderUtils.saveVect(this.s.mvTop, 0, xx, xx + 4, H264Utils.Mv.packMv(mvX, mvY, 0));
MBlockDecoderUtils.saveVect(this.s.mvLeft, 0, 0, 4, H264Utils.Mv.packMv(mvX, mvY, 0));
MBlockDecoderUtils.saveVect(this.s.mvTop, 1, xx, xx + 4, MBlockDecoderUtils.NULL_VECTOR);
MBlockDecoderUtils.saveVect(this.s.mvLeft, 1, 0, 4, MBlockDecoderUtils.NULL_VECTOR);
for (int i = 0; i < 16; i++)
x.setMv(i, 0, H264Utils.Mv.packMv(mvX, mvY, 0));
this.interpolator.getBlockLuma(refs[0][0], mb, 0, (mbX << 6) + mvX, (mbY << 6) + mvY, 16, 16);
PredictionMerger.mergePrediction(this.sh, 0, 0, H264Const.PartPred.L0, 0, mb.getPlaneData(0), null, 0, 16, 16, 16, mb.getPlaneData(0), refs, this.poc);
}
public void decodeChromaSkip(Frame[][] reference, H264Utils.MvList vectors, H264Const.PartPred[] pp, int mbX, int mbY, Picture mb) {
predictChromaInter(reference, vectors, mbX << 3, mbY << 3, 1, mb, pp);
predictChromaInter(reference, vectors, mbX << 3, mbY << 3, 2, mb, pp);
}
}

View file

@ -0,0 +1,120 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
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;
public class PredictionMerger {
public static void mergePrediction(SliceHeader sh, int refIdxL0, int refIdxL1, H264Const.PartPred predType, int comp, byte[] pred0, byte[] pred1, int off, int stride, int blkW, int blkH, byte[] out, Frame[][] refs, int thisPoc) {
PictureParameterSet pps = sh.pps;
if (sh.sliceType == SliceType.P) {
weightPrediction(sh, refIdxL0, comp, pred0, off, stride, blkW, blkH, out);
} else if (!pps.weightedPredFlag || sh.pps.weightedBipredIdc == 0 || (sh.pps.weightedBipredIdc == 2 && predType != H264Const.PartPred.Bi)) {
mergeAvg(pred0, pred1, stride, predType, off, blkW, blkH, out);
} else if (sh.pps.weightedBipredIdc == 1) {
PredictionWeightTable w = sh.predWeightTable;
int w0 = (refIdxL0 == -1) ? 0 : ((comp == 0) ? w.lumaWeight[0][refIdxL0] :
w.chromaWeight[0][comp - 1][refIdxL0]);
int w1 = (refIdxL1 == -1) ? 0 : ((comp == 0) ? w.lumaWeight[1][refIdxL1] :
w.chromaWeight[1][comp - 1][refIdxL1]);
int o0 = (refIdxL0 == -1) ? 0 : ((comp == 0) ? w.lumaOffset[0][refIdxL0] :
w.chromaOffset[0][comp - 1][refIdxL0]);
int o1 = (refIdxL1 == -1) ? 0 : ((comp == 0) ? w.lumaOffset[1][refIdxL1] :
w.chromaOffset[1][comp - 1][refIdxL1]);
mergeWeight(pred0, pred1, stride, predType, off, blkW, blkH, (comp == 0) ? w.lumaLog2WeightDenom :
w.chromaLog2WeightDenom, w0, w1, o0, o1, out);
} else {
int tb = MathUtil.clip(thisPoc - 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 = MathUtil.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);
}
}
public static void weightPrediction(SliceHeader sh, int refIdxL0, int comp, byte[] pred0, int off, int stride, int blkW, int blkH, byte[] out) {
PictureParameterSet pps = sh.pps;
if (pps.weightedPredFlag && sh.predWeightTable != null) {
PredictionWeightTable w = sh.predWeightTable;
weight(pred0, stride, off, blkW, blkH, (comp == 0) ? w.lumaLog2WeightDenom :
w.chromaLog2WeightDenom, (comp == 0) ? w.lumaWeight[0][refIdxL0] :
w.chromaWeight[0][comp - 1][refIdxL0], (comp == 0) ? w.lumaOffset[0][refIdxL0] :
w.chromaOffset[0][comp - 1][refIdxL0], out);
} else {
copyPrediction(pred0, stride, off, blkW, blkH, out);
}
}
private static void mergeAvg(byte[] blk0, byte[] blk1, int stride, H264Const.PartPred p0, int off, int blkW, int blkH, byte[] out) {
if (p0 == H264Const.PartPred.Bi) {
_mergePrediction(blk0, blk1, stride, p0, off, blkW, blkH, out);
} else if (p0 == H264Const.PartPred.L0) {
copyPrediction(blk0, stride, off, blkW, blkH, out);
} else if (p0 == H264Const.PartPred.L1) {
copyPrediction(blk1, stride, off, blkW, blkH, out);
}
}
private static void mergeWeight(byte[] blk0, byte[] blk1, int stride, H264Const.PartPred partPred, int off, int blkW, int blkH, int logWD, int w0, int w1, int o0, int o1, byte[] out) {
if (partPred == H264Const.PartPred.L0) {
weight(blk0, stride, off, blkW, blkH, logWD, w0, o0, out);
} else if (partPred == H264Const.PartPred.L1) {
weight(blk1, stride, off, blkW, blkH, logWD, w1, o1, out);
} else if (partPred == H264Const.PartPred.Bi) {
_weightPrediction(blk0, blk1, stride, off, blkW, blkH, logWD, w0, w1, o0, o1, out);
}
}
private static void copyPrediction(byte[] _in, int stride, int off, int blkW, int blkH, byte[] out) {
for (int i = 0; i < blkH; i++, off += stride - blkW) {
for (int j = 0; j < blkW; j++, off++)
out[off] = _in[off];
}
}
private static void _mergePrediction(byte[] blk0, byte[] blk1, int stride, H264Const.PartPred p0, int off, int blkW, int blkH, byte[] out) {
for (int i = 0; i < blkH; i++, off += stride - blkW) {
for (int j = 0; j < blkW; j++, off++)
out[off] = (byte)(blk0[off] + blk1[off] + 1 >> 1);
}
}
private static void _weightPrediction(byte[] blk0, byte[] blk1, int stride, int off, int blkW, int blkH, int logWD, int w0, int w1, int o0, int o1, byte[] out) {
int round = (1 << logWD) + (w0 + w1 << 7);
int sum = (o0 + o1 + 1 >> 1) - 128;
int logWDCP1 = logWD + 1;
for (int i = 0; i < blkH; i++, off += stride - blkW) {
for (int j = 0; j < blkW; j++, off++)
out[off] = (byte)MathUtil.clip((blk0[off] * w0 + blk1[off] * w1 + round >> logWDCP1) + sum, -128, 127);
}
}
private static void weight(byte[] blk0, int stride, int off, int blkW, int blkH, int logWD, int w, int o, byte[] out) {
int round = 1 << logWD - 1;
if (logWD >= 1) {
o -= 128;
round += w << 7;
for (int i = 0; i < blkH; i++, off += stride - blkW) {
for (int j = 0; j < blkW; j++, off++)
out[off] = (byte)MathUtil.clip((blk0[off] * w + round >> logWD) + o, -128, 127);
}
} else {
o += (w << 7) - 128;
for (int i = 0; i < blkH; i++, off += stride - blkW) {
for (int j = 0; j < blkW; j++, off++)
out[off] = (byte)MathUtil.clip(blk0[off] * w + o, -128, 127);
}
}
}
}

View file

@ -0,0 +1,151 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import java.util.Comparator;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.io.model.Frame;
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.model.Picture;
import org.jcodec.common.tools.MathUtil;
import org.jcodec.platform.Platform;
public class RefListManager {
private SliceHeader sh;
private int[] numRef;
private Frame[] sRefs;
private IntObjectMap<Frame> lRefs;
private Frame frameOut;
public RefListManager(SliceHeader sh, Frame[] sRefs, IntObjectMap<Frame> lRefs, Frame frameOut) {
this.sh = sh;
this.sRefs = sRefs;
this.lRefs = lRefs;
if (sh.numRefIdxActiveOverrideFlag) {
this.numRef = new int[] { sh.numRefIdxActiveMinus1[0] + 1, sh.numRefIdxActiveMinus1[1] + 1 };
} else {
this.numRef = new int[] { sh.pps.numRefIdxActiveMinus1[0] + 1, sh.pps.numRefIdxActiveMinus1[1] + 1 };
}
this.frameOut = frameOut;
}
public Frame[][] getRefList() {
Frame[][] refList = null;
if (this.sh.sliceType == SliceType.P) {
refList = new Frame[][] { buildRefListP(), null };
} else if (this.sh.sliceType == SliceType.B) {
refList = buildRefListB();
}
MBlockDecoderUtils.debugPrint("------");
if (refList != null)
for (int l = 0; l < 2; l++) {
if (refList[l] != null)
for (int i = 0; i < (refList[l]).length; i++) {
if (refList[l][i] != null)
MBlockDecoderUtils.debugPrint("REF[%d][%d]: ", l, i, refList[l][i].getPOC());
}
}
return refList;
}
private Frame[] buildRefListP() {
int frame_num = this.sh.frameNum;
int maxFrames = 1 << this.sh.sps.log2MaxFrameNumMinus4 + 4;
Frame[] result = new Frame[this.numRef[0]];
int refs = 0;
for (int i = frame_num - 1; i >= frame_num - maxFrames && refs < this.numRef[0]; i--) {
int fn = (i < 0) ? (i + maxFrames) : i;
if (this.sRefs[fn] != null) {
result[refs] = (this.sRefs[fn] == H264Const.NO_PIC) ? null : this.sRefs[fn];
refs++;
}
}
int[] keys = this.lRefs.keys();
Arrays.sort(keys);
for (int j = 0; j < keys.length && refs < this.numRef[0]; j++)
result[refs++] = this.lRefs.get(keys[j]);
reorder(result, 0);
return result;
}
private Frame[][] buildRefListB() {
Frame[] l0 = buildList(Frame.POCDesc, Frame.POCAsc);
Frame[] l1 = buildList(Frame.POCAsc, Frame.POCDesc);
if (Platform.arrayEqualsObj(l0, l1) && count(l1) > 1) {
Frame frame = l1[1];
l1[1] = l1[0];
l1[0] = frame;
}
Frame[][] result = { Platform.<Frame>copyOfObj(l0, this.numRef[0]), Platform.<Frame>copyOfObj(l1, this.numRef[1]) };
reorder(result[0], 0);
reorder(result[1], 1);
return result;
}
private Frame[] buildList(Comparator<Frame> cmpFwd, Comparator<Frame> cmpInv) {
Frame[] refs = new Frame[this.sRefs.length + this.lRefs.size()];
Frame[] fwd = copySort(cmpFwd, this.frameOut);
Frame[] inv = copySort(cmpInv, this.frameOut);
int nFwd = count(fwd);
int nInv = count(inv);
int ref = 0;
for (int j = 0; j < nFwd; j++, ref++)
refs[ref] = fwd[j];
for (int i = 0; i < nInv; i++, ref++)
refs[ref] = inv[i];
int[] keys = this.lRefs.keys();
Arrays.sort(keys);
for (int k = 0; k < keys.length; k++, ref++)
refs[ref] = this.lRefs.get(keys[k]);
return refs;
}
private int count(Frame[] arr) {
for (int nn = 0; nn < arr.length; nn++) {
if (arr[nn] == null)
return nn;
}
return arr.length;
}
private Frame[] copySort(Comparator<Frame> fwd, Frame dummy) {
Frame[] copyOf = Platform.<Frame>copyOfObj(this.sRefs, this.sRefs.length);
for (int i = 0; i < copyOf.length; i++) {
if (fwd.compare(dummy, copyOf[i]) > 0)
copyOf[i] = null;
}
Arrays.sort(copyOf, fwd);
return copyOf;
}
private void reorder(Picture[] result, int list) {
if (this.sh.refPicReordering[list] == null)
return;
int predict = this.sh.frameNum;
int maxFrames = 1 << this.sh.sps.log2MaxFrameNumMinus4 + 4;
for (int ind = 0; ind < (this.sh.refPicReordering[list][0]).length; ind++) {
switch (this.sh.refPicReordering[list][0][ind]) {
case 0:
predict = MathUtil.wrap(predict - this.sh.refPicReordering[list][1][ind] - 1, maxFrames);
break;
case 1:
predict = MathUtil.wrap(predict + this.sh.refPicReordering[list][1][ind] + 1, maxFrames);
break;
case 2:
throw new RuntimeException("long term");
}
for (int k = this.numRef[list] - 1; k > ind; k--)
result[k] = result[k - 1];
result[ind] = this.sRefs[predict];
for (int i = ind + 1, j = i; i < this.numRef[list] && result[i] != null; i++) {
if (result[i] != this.sRefs[predict])
result[j++] = result[i];
}
}
}
}

View file

@ -0,0 +1,190 @@
package org.jcodec.codecs.h264.decode;
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.Picture;
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) {
this.parser = sliceReader;
initContext();
MBlockDecoderUtils.debugPrint("============%d============= ", this.frameOut.getPOC());
Frame[][] refList = this.refListManager.getRefList();
decodeMacroblocks(refList);
}
private void initContext() {
SliceHeader sh = this.parser.getSliceHeader();
this.decoderState = new DecoderState(sh);
this.mapper = new MapManager(sh.sps, sh.pps).getMapper(sh);
this.decoderIntra16x16 = new MBlockDecoderIntra16x16(this.mapper, sh, this.di, this.frameOut.getPOC(), this.decoderState);
this.decoderIntraNxN = new MBlockDecoderIntraNxN(this.mapper, sh, this.di, this.frameOut.getPOC(), this.decoderState);
this.decoderInter = new MBlockDecoderInter(this.mapper, sh, this.di, this.frameOut.getPOC(), this.decoderState);
this.decoderBDirect = new MBlockDecoderBDirect(this.mapper, sh, this.di, this.frameOut.getPOC(), this.decoderState);
this.decoderInter8x8 = new MBlockDecoderInter8x8(this.mapper, this.decoderBDirect, sh, this.di, this.frameOut.getPOC(), this.decoderState);
this.skipDecoder = new MBlockSkipDecoder(this.mapper, this.decoderBDirect, sh, this.di, this.frameOut.getPOC(), this.decoderState);
this.decoderIPCM = new MBlockDecoderIPCM(this.mapper, this.decoderState);
this.refListManager = new RefListManager(sh, this.sRefs, this.lRefs, this.frameOut);
}
private void decodeMacroblocks(Frame[][] refList) {
Picture mb = Picture.create(16, 16, this.activeSps.chromaFormatIdc);
int mbWidth = this.activeSps.picWidthInMbsMinus1 + 1;
MBlock mBlock = new MBlock(this.activeSps.chromaFormatIdc);
while (this.parser.readMacroblock(mBlock)) {
decode(mBlock, (this.parser.getSliceHeader()).sliceType, mb, refList);
int mbAddr = this.mapper.getAddress(mBlock.mbIdx);
int mbX = mbAddr % mbWidth;
int mbY = mbAddr / mbWidth;
putMacroblock(this.frameOut, mb, mbX, mbY);
this.di.shs[mbAddr] = this.parser.getSliceHeader();
this.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;
this.di.nCoeff[blkY][blkX] = mBlock.nCoeff[i];
}
}
public void decode(MBlock mBlock, SliceType sliceType, Picture mb, Frame[][] references) {
if (mBlock.skipped) {
this.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, Picture mb) {
decodeMBlockIInt(mBlock, mb);
}
private void decodeMBlockIInt(MBlock mBlock, Picture mb) {
if (mBlock.curMbType == MBType.I_NxN) {
this.decoderIntraNxN.decode(mBlock, mb);
} else if (mBlock.curMbType == MBType.I_16x16) {
this.decoderIntra16x16.decode(mBlock, mb);
} else {
Logger.warn("IPCM macroblock found. Not tested, may cause unpredictable behavior.");
this.decoderIPCM.decode(mBlock, mb);
}
}
private void decodeMBlockP(MBlock mBlock, Picture mb, Frame[][] references) {
if (MBType.P_16x16 == mBlock.curMbType) {
this.decoderInter.decode16x16(mBlock, mb, references, H264Const.PartPred.L0);
} else if (MBType.P_16x8 == mBlock.curMbType) {
this.decoderInter.decode16x8(mBlock, mb, references, H264Const.PartPred.L0, H264Const.PartPred.L0);
} else if (MBType.P_8x16 == mBlock.curMbType) {
this.decoderInter.decode8x16(mBlock, mb, references, H264Const.PartPred.L0, H264Const.PartPred.L0);
} else if (MBType.P_8x8 == mBlock.curMbType) {
this.decoderInter8x8.decode(mBlock, references, mb, SliceType.P, false);
} else if (MBType.P_8x8ref0 == mBlock.curMbType) {
this.decoderInter8x8.decode(mBlock, references, mb, SliceType.P, true);
} else {
decodeMBlockIInt(mBlock, mb);
}
}
private void decodeMBlockB(MBlock mBlock, Picture mb, Frame[][] references) {
if (mBlock.curMbType.isIntra()) {
decodeMBlockIInt(mBlock, mb);
} else if (mBlock.curMbType == MBType.B_Direct_16x16) {
this.decoderBDirect.decode(mBlock, mb, references);
} else if (mBlock.mbType <= 3) {
this.decoderInter.decode16x16(mBlock, mb, references, H264Const.bPredModes[mBlock.mbType][0]);
} else if (mBlock.mbType == 22) {
this.decoderInter8x8.decode(mBlock, references, mb, SliceType.B, false);
} else if ((mBlock.mbType & 0x1) == 0) {
this.decoderInter.decode16x8(mBlock, mb, references, H264Const.bPredModes[mBlock.mbType][0], H264Const.bPredModes[mBlock.mbType][1]);
} else {
this.decoderInter.decode8x16(mBlock, mb, references, H264Const.bPredModes[mBlock.mbType][0], H264Const.bPredModes[mBlock.mbType][1]);
}
}
private static void putMacroblock(Picture tgt, Picture 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;
int mbx16 = mbX * 16;
int mby16 = mbY * 16;
byte[] decodedY = decoded.getPlaneData(0);
for (int i = 0; i < 16; i++) {
System.arraycopy(decodedY, dOff, luma, (mby16 + i) * stride + mbx16, 16);
dOff += 16;
}
int mbx8 = mbX * 8;
int mby8 = mbY * 8;
byte[] decodedCb = decoded.getPlaneData(1);
byte[] decodedCr = decoded.getPlaneData(2);
for (int j = 0; j < 8; j++) {
int decodePos = j << 3;
int chromaPos = (mby8 + j) * strideChroma + mbx8;
System.arraycopy(decodedCb, decodePos, cb, chromaPos, 8);
System.arraycopy(decodedCr, decodePos, cr, chromaPos, 8);
}
}
}

View file

@ -0,0 +1,219 @@
package org.jcodec.codecs.h264.decode;
import java.util.ArrayList;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.PredictionWeightTable;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
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.IntArrayList;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.model.ColorSpace;
public class SliceHeaderReader {
public static SliceHeader readPart1(BitReader _in) {
SliceHeader sh = new SliceHeader();
sh.firstMbInSlice = CAVLCReader.readUEtrace(_in, "SH: first_mb_in_slice");
int shType = CAVLCReader.readUEtrace(_in, "SH: slice_type");
sh.sliceType = SliceType.fromValue(shType % 5);
sh.sliceTypeRestr = (shType / 5 > 0);
sh.picParameterSetId = CAVLCReader.readUEtrace(_in, "SH: pic_parameter_set_id");
return sh;
}
public static SliceHeader readPart2(SliceHeader sh, NALUnit nalUnit, SeqParameterSet sps, PictureParameterSet pps, BitReader _in) {
sh.pps = pps;
sh.sps = sps;
sh.frameNum = CAVLCReader.readU(_in, sps.log2MaxFrameNumMinus4 + 4, "SH: frame_num");
if (!sps.frameMbsOnlyFlag) {
sh.fieldPicFlag = CAVLCReader.readBool(_in, "SH: field_pic_flag");
if (sh.fieldPicFlag)
sh.bottomFieldFlag = CAVLCReader.readBool(_in, "SH: bottom_field_flag");
}
if (nalUnit.type == NALUnitType.IDR_SLICE)
sh.idrPicId = CAVLCReader.readUEtrace(_in, "SH: idr_pic_id");
if (sps.picOrderCntType == 0) {
sh.picOrderCntLsb = CAVLCReader.readU(_in, sps.log2MaxPicOrderCntLsbMinus4 + 4, "SH: pic_order_cnt_lsb");
if (pps.picOrderPresentFlag && !sps.fieldPicFlag)
sh.deltaPicOrderCntBottom = CAVLCReader.readSE(_in, "SH: delta_pic_order_cnt_bottom");
}
sh.deltaPicOrderCnt = new int[2];
if (sps.picOrderCntType == 1 && !sps.deltaPicOrderAlwaysZeroFlag) {
sh.deltaPicOrderCnt[0] = CAVLCReader.readSE(_in, "SH: delta_pic_order_cnt[0]");
if (pps.picOrderPresentFlag && !sps.fieldPicFlag)
sh.deltaPicOrderCnt[1] = CAVLCReader.readSE(_in, "SH: delta_pic_order_cnt[1]");
}
if (pps.redundantPicCntPresentFlag)
sh.redundantPicCnt = CAVLCReader.readUEtrace(_in, "SH: redundant_pic_cnt");
if (sh.sliceType == SliceType.B)
sh.directSpatialMvPredFlag = CAVLCReader.readBool(_in, "SH: direct_spatial_mv_pred_flag");
if (sh.sliceType == SliceType.P || sh.sliceType == SliceType.SP || sh.sliceType == SliceType.B) {
sh.numRefIdxActiveOverrideFlag = CAVLCReader.readBool(_in, "SH: num_ref_idx_active_override_flag");
if (sh.numRefIdxActiveOverrideFlag) {
sh.numRefIdxActiveMinus1[0] = CAVLCReader.readUEtrace(_in, "SH: num_ref_idx_l0_active_minus1");
if (sh.sliceType == SliceType.B)
sh.numRefIdxActiveMinus1[1] = CAVLCReader.readUEtrace(_in, "SH: num_ref_idx_l1_active_minus1");
}
}
readRefPicListReordering(sh, _in);
if ((pps.weightedPredFlag && (sh.sliceType == SliceType.P || sh.sliceType == SliceType.SP)) || (pps.weightedBipredIdc == 1 && sh.sliceType == SliceType.B))
readPredWeightTable(sps, pps, sh, _in);
if (nalUnit.nal_ref_idc != 0)
readDecoderPicMarking(nalUnit, sh, _in);
if (pps.entropyCodingModeFlag && sh.sliceType.isInter())
sh.cabacInitIdc = CAVLCReader.readUEtrace(_in, "SH: cabac_init_idc");
sh.sliceQpDelta = CAVLCReader.readSE(_in, "SH: slice_qp_delta");
if (sh.sliceType == SliceType.SP || sh.sliceType == SliceType.SI) {
if (sh.sliceType == SliceType.SP)
sh.spForSwitchFlag = CAVLCReader.readBool(_in, "SH: sp_for_switch_flag");
sh.sliceQsDelta = CAVLCReader.readSE(_in, "SH: slice_qs_delta");
}
if (pps.deblockingFilterControlPresentFlag) {
sh.disableDeblockingFilterIdc = CAVLCReader.readUEtrace(_in, "SH: disable_deblocking_filter_idc");
if (sh.disableDeblockingFilterIdc != 1) {
sh.sliceAlphaC0OffsetDiv2 = CAVLCReader.readSE(_in, "SH: slice_alpha_c0_offset_div2");
sh.sliceBetaOffsetDiv2 = CAVLCReader.readSE(_in, "SH: slice_beta_offset_div2");
}
}
if (pps.numSliceGroupsMinus1 > 0 && pps.sliceGroupMapType >= 3 && pps.sliceGroupMapType <= 5) {
int len = SeqParameterSet.getPicHeightInMbs(sps) * (sps.picWidthInMbsMinus1 + 1) / (pps.sliceGroupChangeRateMinus1 + 1);
if (SeqParameterSet.getPicHeightInMbs(sps) * (sps.picWidthInMbsMinus1 + 1) % (pps.sliceGroupChangeRateMinus1 + 1) > 0)
len++;
len = CeilLog2(len + 1);
sh.sliceGroupChangeCycle = CAVLCReader.readU(_in, len, "SH: slice_group_change_cycle");
}
return sh;
}
private static int CeilLog2(int uiVal) {
int uiTmp = uiVal - 1;
int uiRet = 0;
while (uiTmp != 0) {
uiTmp >>= 1;
uiRet++;
}
return uiRet;
}
private static void readDecoderPicMarking(NALUnit nalUnit, SliceHeader sh, BitReader _in) {
if (nalUnit.type == NALUnitType.IDR_SLICE) {
boolean noOutputOfPriorPicsFlag = CAVLCReader.readBool(_in, "SH: no_output_of_prior_pics_flag");
boolean longTermReferenceFlag = CAVLCReader.readBool(_in, "SH: long_term_reference_flag");
sh.refPicMarkingIDR = new RefPicMarkingIDR(noOutputOfPriorPicsFlag, longTermReferenceFlag);
} else {
boolean adaptiveRefPicMarkingModeFlag = CAVLCReader.readBool(_in, "SH: adaptive_ref_pic_marking_mode_flag");
if (adaptiveRefPicMarkingModeFlag) {
ArrayList<RefPicMarking.Instruction> mmops = new ArrayList<>();
while (true) {
int memoryManagementControlOperation = CAVLCReader.readUEtrace(_in, "SH: memory_management_control_operation");
RefPicMarking.Instruction instr = null;
switch (memoryManagementControlOperation) {
case 1:
instr = new RefPicMarking.Instruction(RefPicMarking.InstrType.REMOVE_SHORT, CAVLCReader.readUEtrace(_in, "SH: difference_of_pic_nums_minus1") + 1, 0);
break;
case 2:
instr = new RefPicMarking.Instruction(RefPicMarking.InstrType.REMOVE_LONG, CAVLCReader.readUEtrace(_in, "SH: long_term_pic_num"), 0);
break;
case 3:
instr = new RefPicMarking.Instruction(RefPicMarking.InstrType.CONVERT_INTO_LONG, CAVLCReader.readUEtrace(_in, "SH: difference_of_pic_nums_minus1") + 1, CAVLCReader.readUEtrace(_in, "SH: long_term_frame_idx"));
break;
case 4:
instr = new RefPicMarking.Instruction(RefPicMarking.InstrType.TRUNK_LONG, CAVLCReader.readUEtrace(_in, "SH: max_long_term_frame_idx_plus1") - 1, 0);
break;
case 5:
instr = new RefPicMarking.Instruction(RefPicMarking.InstrType.CLEAR, 0, 0);
break;
case 6:
instr = new RefPicMarking.Instruction(RefPicMarking.InstrType.MARK_LONG, CAVLCReader.readUEtrace(_in, "SH: long_term_frame_idx"), 0);
break;
}
if (instr != null)
mmops.add(instr);
if (memoryManagementControlOperation == 0) {
sh.refPicMarkingNonIDR = new RefPicMarking(mmops.<RefPicMarking.Instruction>toArray(new RefPicMarking.Instruction[0]));
break;
}
}
}
}
}
private static void readPredWeightTable(SeqParameterSet sps, PictureParameterSet pps, SliceHeader sh, BitReader _in) {
sh.predWeightTable = new PredictionWeightTable();
int[] numRefsMinus1 = sh.numRefIdxActiveOverrideFlag ? sh.numRefIdxActiveMinus1 :
pps.numRefIdxActiveMinus1;
int[] nr = { numRefsMinus1[0] + 1, numRefsMinus1[1] + 1 };
sh.predWeightTable.lumaLog2WeightDenom = CAVLCReader.readUEtrace(_in, "SH: luma_log2_weight_denom");
if (sps.chromaFormatIdc != ColorSpace.MONO)
sh.predWeightTable.chromaLog2WeightDenom = CAVLCReader.readUEtrace(_in, "SH: chroma_log2_weight_denom");
int defaultLW = 1 << sh.predWeightTable.lumaLog2WeightDenom;
int defaultCW = 1 << sh.predWeightTable.chromaLog2WeightDenom;
for (int list = 0; list < 2; list++) {
sh.predWeightTable.lumaWeight[list] = new int[nr[list]];
sh.predWeightTable.lumaOffset[list] = new int[nr[list]];
sh.predWeightTable.chromaWeight[list] = new int[2][nr[list]];
sh.predWeightTable.chromaOffset[list] = new int[2][nr[list]];
for (int i = 0; i < nr[list]; i++) {
sh.predWeightTable.lumaWeight[list][i] = defaultLW;
sh.predWeightTable.lumaOffset[list][i] = 0;
sh.predWeightTable.chromaWeight[list][0][i] = defaultCW;
sh.predWeightTable.chromaOffset[list][0][i] = 0;
sh.predWeightTable.chromaWeight[list][1][i] = defaultCW;
sh.predWeightTable.chromaOffset[list][1][i] = 0;
}
}
readWeightOffset(sps, pps, sh, _in, nr, 0);
if (sh.sliceType == SliceType.B)
readWeightOffset(sps, pps, sh, _in, nr, 1);
}
private static void readWeightOffset(SeqParameterSet sps, PictureParameterSet pps, SliceHeader sh, BitReader _in, int[] numRefs, int list) {
for (int i = 0; i < numRefs[list]; i++) {
boolean lumaWeightL0Flag = CAVLCReader.readBool(_in, "SH: luma_weight_l0_flag");
if (lumaWeightL0Flag) {
sh.predWeightTable.lumaWeight[list][i] = CAVLCReader.readSE(_in, "SH: weight");
sh.predWeightTable.lumaOffset[list][i] = CAVLCReader.readSE(_in, "SH: offset");
}
if (sps.chromaFormatIdc != ColorSpace.MONO) {
boolean chromaWeightL0Flag = CAVLCReader.readBool(_in, "SH: chroma_weight_l0_flag");
if (chromaWeightL0Flag) {
sh.predWeightTable.chromaWeight[list][0][i] = CAVLCReader.readSE(_in, "SH: weight");
sh.predWeightTable.chromaOffset[list][0][i] = CAVLCReader.readSE(_in, "SH: offset");
sh.predWeightTable.chromaWeight[list][1][i] = CAVLCReader.readSE(_in, "SH: weight");
sh.predWeightTable.chromaOffset[list][1][i] = CAVLCReader.readSE(_in, "SH: offset");
}
}
}
}
private static void readRefPicListReordering(SliceHeader sh, BitReader _in) {
sh.refPicReordering = new int[2][][];
if (sh.sliceType.isInter()) {
boolean refPicListReorderingFlagL0 = CAVLCReader.readBool(_in, "SH: ref_pic_list_reordering_flag_l0");
if (refPicListReorderingFlagL0)
sh.refPicReordering[0] = readReorderingEntries(_in);
}
if (sh.sliceType == SliceType.B) {
boolean refPicListReorderingFlagL1 = CAVLCReader.readBool(_in, "SH: ref_pic_list_reordering_flag_l1");
if (refPicListReorderingFlagL1)
sh.refPicReordering[1] = readReorderingEntries(_in);
}
}
private static int[][] readReorderingEntries(BitReader _in) {
IntArrayList ops = IntArrayList.createIntArrayList();
IntArrayList args = IntArrayList.createIntArrayList();
while (true) {
int idc = CAVLCReader.readUEtrace(_in, "SH: reordering_of_pic_nums_idc");
if (idc == 3)
break;
ops.add(idc);
args.add(CAVLCReader.readUEtrace(_in, "SH: abs_diff_pic_num_minus1"));
}
return new int[][] { ops.toArray(), args.toArray() };
}
}

View file

@ -0,0 +1,857 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.common.biari.MDecoder;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.CABAC;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
public class SliceReader {
private PictureParameterSet activePps;
private CABAC cabac;
private MDecoder mDecoder;
private CAVLC[] cavlc;
private BitReader reader;
private Mapper mapper;
private SliceHeader sh;
private NALUnit nalUnit;
private boolean prevMbSkipped = false;
private int mbIdx;
private MBType prevMBType = null;
private int mbSkipRun;
private boolean endOfData;
MBType[] topMBType;
MBType leftMBType;
int leftCBPLuma;
int[] topCBPLuma;
int leftCBPChroma;
int[] topCBPChroma;
ColorSpace chromaFormat;
boolean transform8x8;
int[] numRef;
boolean tf8x8Left;
boolean[] tf8x8Top;
int[] i4x4PredTop;
int[] i4x4PredLeft;
H264Const.PartPred[] predModeLeft;
H264Const.PartPred[] predModeTop;
public SliceReader(PictureParameterSet activePps, CABAC cabac, CAVLC[] cavlc, MDecoder mDecoder, BitReader reader, Mapper mapper, SliceHeader sh, NALUnit nalUnit) {
this.activePps = activePps;
this.cabac = cabac;
this.mDecoder = mDecoder;
this.cavlc = cavlc;
this.reader = reader;
this.mapper = mapper;
this.sh = sh;
this.nalUnit = nalUnit;
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
this.topMBType = new MBType[mbWidth];
this.topCBPLuma = new int[mbWidth];
this.topCBPChroma = new int[mbWidth];
this.chromaFormat = sh.sps.chromaFormatIdc;
this.transform8x8 = (sh.pps.extended == null) ? false : sh.pps.extended.transform8x8ModeFlag;
if (sh.numRefIdxActiveOverrideFlag) {
this.numRef = new int[] { sh.numRefIdxActiveMinus1[0] + 1, sh.numRefIdxActiveMinus1[1] + 1 };
} else {
this.numRef = new int[] { sh.pps.numRefIdxActiveMinus1[0] + 1, sh.pps.numRefIdxActiveMinus1[1] + 1 };
}
this.tf8x8Top = new boolean[mbWidth];
this.predModeLeft = new H264Const.PartPred[2];
this.predModeTop = new H264Const.PartPred[mbWidth << 1];
this.i4x4PredLeft = new int[4];
this.i4x4PredTop = new int[mbWidth << 2];
}
public boolean readMacroblock(MBlock mBlock) {
int mbWidth = this.sh.sps.picWidthInMbsMinus1 + 1;
int mbHeight = this.sh.sps.picHeightInMapUnitsMinus1 + 1;
if ((this.endOfData && this.mbSkipRun == 0) || this.mbIdx >= mbWidth * mbHeight)
return false;
mBlock.mbIdx = this.mbIdx;
mBlock.prevMbType = this.prevMBType;
boolean mbaffFrameFlag = (this.sh.sps.mbAdaptiveFrameFieldFlag && !this.sh.fieldPicFlag);
if (this.sh.sliceType.isInter() && !this.activePps.entropyCodingModeFlag) {
if (!this.prevMbSkipped && this.mbSkipRun == 0) {
this.mbSkipRun = CAVLCReader.readUEtrace(this.reader, "mb_skip_run");
if (!CAVLCReader.moreRBSPData(this.reader))
this.endOfData = true;
}
if (this.mbSkipRun > 0) {
this.mbSkipRun--;
int i = this.mapper.getAddress(this.mbIdx);
this.prevMbSkipped = true;
this.prevMBType = null;
MBlockDecoderUtils.debugPrint("---------------------- MB (%d,%d) ---------------------", i % mbWidth, i / mbWidth);
mBlock.skipped = true;
int j = this.mapper.getMbX(mBlock.mbIdx);
this.topMBType[j] = this.leftMBType = null;
int blk8x8X = j << 1;
this.predModeLeft[0] = this.predModeLeft[1] = this.predModeTop[blk8x8X] = this.predModeTop[blk8x8X + 1] = H264Const.PartPred.L0;
this.mbIdx++;
return true;
}
this.prevMbSkipped = false;
}
int mbAddr = this.mapper.getAddress(this.mbIdx);
int mbX = mbAddr % mbWidth;
int mbY = mbAddr / mbWidth;
MBlockDecoderUtils.debugPrint("---------------------- MB (%d,%d) ---------------------", mbX, mbY);
if (this.sh.sliceType.isIntra() || !this.activePps.entropyCodingModeFlag ||
!readMBSkipFlag(this.sh.sliceType, this.mapper.leftAvailable(this.mbIdx),
this.mapper.topAvailable(this.mbIdx), mbX)) {
boolean mb_field_decoding_flag = false;
if (mbaffFrameFlag && (this.mbIdx % 2 == 0 || (this.mbIdx % 2 == 1 && this.prevMbSkipped)))
mb_field_decoding_flag = CAVLCReader.readBool(this.reader, "mb_field_decoding_flag");
mBlock.fieldDecoding = mb_field_decoding_flag;
readMBlock(mBlock, this.sh.sliceType);
this.prevMBType = mBlock.curMbType;
} else {
this.prevMBType = null;
this.prevMbSkipped = true;
mBlock.skipped = true;
int blk8x8X = mbX << 1;
this.predModeLeft[0] = this.predModeLeft[1] = this.predModeTop[blk8x8X] = this.predModeTop[blk8x8X + 1] = H264Const.PartPred.L0;
}
this
.endOfData = ((this.activePps.entropyCodingModeFlag && this.mDecoder.decodeFinalBin() == 1) || (!this.activePps.entropyCodingModeFlag && !CAVLCReader.moreRBSPData(this.reader)));
this.mbIdx++;
this.topMBType[this.mapper.getMbX(mBlock.mbIdx)] = this.leftMBType = mBlock.curMbType;
return true;
}
int readMBQpDelta(MBType prevMbType) {
int mbQPDelta;
if (!this.activePps.entropyCodingModeFlag) {
mbQPDelta = CAVLCReader.readSE(this.reader, "mb_qp_delta");
} else {
mbQPDelta = this.cabac.readMBQpDelta(this.mDecoder, prevMbType);
}
return mbQPDelta;
}
int readChromaPredMode(int mbX, boolean leftAvailable, boolean topAvailable) {
int chromaPredictionMode;
if (!this.activePps.entropyCodingModeFlag) {
chromaPredictionMode = CAVLCReader.readUEtrace(this.reader, "MBP: intra_chroma_pred_mode");
} else {
chromaPredictionMode = this.cabac.readIntraChromaPredMode(this.mDecoder, mbX, this.leftMBType, this.topMBType[mbX], leftAvailable, topAvailable);
}
return chromaPredictionMode;
}
boolean readTransform8x8Flag(boolean leftAvailable, boolean topAvailable, MBType leftType, MBType topType, boolean is8x8Left, boolean is8x8Top) {
if (!this.activePps.entropyCodingModeFlag)
return CAVLCReader.readBool(this.reader, "transform_size_8x8_flag");
return this.cabac.readTransform8x8Flag(this.mDecoder, leftAvailable, topAvailable, leftType, topType, is8x8Left, is8x8Top);
}
protected int readCodedBlockPatternIntra(boolean leftAvailable, boolean topAvailable, int leftCBP, int topCBP, MBType leftMB, MBType topMB) {
if (!this.activePps.entropyCodingModeFlag)
return H264Const.CODED_BLOCK_PATTERN_INTRA_COLOR[CAVLCReader.readUEtrace(this.reader, "coded_block_pattern")];
return this.cabac.codedBlockPatternIntra(this.mDecoder, leftAvailable, topAvailable, leftCBP, topCBP, leftMB, topMB);
}
protected int readCodedBlockPatternInter(boolean leftAvailable, boolean topAvailable, int leftCBP, int topCBP, MBType leftMB, MBType topMB) {
if (!this.activePps.entropyCodingModeFlag) {
int code = CAVLCReader.readUEtrace(this.reader, "coded_block_pattern");
return H264Const.CODED_BLOCK_PATTERN_INTER_COLOR[code];
}
return this.cabac.codedBlockPatternIntra(this.mDecoder, leftAvailable, topAvailable, leftCBP, topCBP, leftMB, topMB);
}
int readRefIdx(boolean leftAvailable, boolean topAvailable, MBType leftType, MBType topType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred curPred, int mbX, int partX, int partY, int partW, int partH, int list) {
if (!this.activePps.entropyCodingModeFlag)
return CAVLCReader.readTE(this.reader, this.numRef[list] - 1);
return this.cabac.readRefIdx(this.mDecoder, leftAvailable, topAvailable, leftType, topType, leftPred, topPred, curPred, mbX, partX, partY, partW, partH, list);
}
int readMVD(int comp, boolean leftAvailable, boolean topAvailable, MBType leftType, MBType topType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred curPred, int mbX, int partX, int partY, int partW, int partH, int list) {
if (!this.activePps.entropyCodingModeFlag)
return CAVLCReader.readSE(this.reader, "mvd_l0_x");
return this.cabac.readMVD(this.mDecoder, comp, leftAvailable, topAvailable, leftType, topType, leftPred, topPred, curPred, mbX, partX, partY, partW, partH, list);
}
int readPredictionI4x4Block(boolean leftAvailable, boolean topAvailable, MBType leftMBType, MBType topMBType, int blkX, int blkY, int mbX) {
int mode = 2;
if ((leftAvailable || blkX > 0) && (topAvailable || blkY > 0)) {
int predModeB = (topMBType == MBType.I_NxN || blkY > 0) ? this.i4x4PredTop[(mbX << 2) + blkX] : 2;
int predModeA = (leftMBType == MBType.I_NxN || blkX > 0) ? this.i4x4PredLeft[blkY] : 2;
mode = Math.min(predModeB, predModeA);
}
if (!prev4x4PredMode()) {
int rem_intra4x4_pred_mode = rem4x4PredMode();
mode = rem_intra4x4_pred_mode + ((rem_intra4x4_pred_mode < mode) ? 0 : 1);
}
this.i4x4PredLeft[blkY] = mode;
this.i4x4PredTop[(mbX << 2) + blkX] = mode;
return mode;
}
int rem4x4PredMode() {
if (!this.activePps.entropyCodingModeFlag)
return CAVLCReader.readNBit(this.reader, 3, "MB: rem_intra4x4_pred_mode");
return this.cabac.rem4x4PredMode(this.mDecoder);
}
boolean prev4x4PredMode() {
if (!this.activePps.entropyCodingModeFlag)
return CAVLCReader.readBool(this.reader, "MBP: prev_intra4x4_pred_mode_flag");
return this.cabac.prev4x4PredModeFlag(this.mDecoder);
}
void read16x16DC(boolean leftAvailable, boolean topAvailable, int mbX, int[] dc) {
if (!this.activePps.entropyCodingModeFlag) {
this.cavlc[0].readLumaDCBlock(this.reader, dc, mbX, leftAvailable, this.leftMBType, topAvailable, this.topMBType[mbX], CoeffTransformer.zigzag4x4);
} else if (this.cabac.readCodedBlockFlagLumaDC(this.mDecoder, mbX, this.leftMBType, this.topMBType[mbX], leftAvailable, topAvailable, MBType.I_16x16) == 1) {
this.cabac.readCoeffs(this.mDecoder, CABAC.BlockType.LUMA_16_DC, dc, 0, 16, CoeffTransformer.zigzag4x4, H264Const.identityMapping16, H264Const.identityMapping16);
}
}
int read16x16AC(boolean leftAvailable, boolean topAvailable, int mbX, int cbpLuma, int[] ac, int blkOffLeft, int blkOffTop, int blkX, int blkY) {
if (!this.activePps.entropyCodingModeFlag)
return this.cavlc[0].readACBlock(this.reader, ac, blkX, blkOffTop, (blkOffLeft != 0 || leftAvailable),
(blkOffLeft == 0) ? this.leftMBType : MBType.I_16x16, (blkOffTop != 0 || topAvailable),
(blkOffTop == 0) ? this.topMBType[mbX] : MBType.I_16x16, 1, 15, CoeffTransformer.zigzag4x4);
if (this.cabac.readCodedBlockFlagLumaAC(this.mDecoder, CABAC.BlockType.LUMA_15_AC, blkX, blkOffTop, 0, this.leftMBType, this.topMBType[mbX], leftAvailable, topAvailable, this.leftCBPLuma, this.topCBPLuma[mbX], cbpLuma, MBType.I_16x16) == 1)
return this.cabac.readCoeffs(this.mDecoder, CABAC.BlockType.LUMA_15_AC, ac, 1, 15, CoeffTransformer.zigzag4x4, H264Const.identityMapping16, H264Const.identityMapping16);
return 0;
}
int readResidualAC(boolean leftAvailable, boolean topAvailable, int mbX, MBType curMbType, int cbpLuma, int blkOffLeft, int blkOffTop, int blkX, int blkY, int[] ac) {
if (!this.activePps.entropyCodingModeFlag) {
if (this.reader.remaining() <= 0)
return 0;
return this.cavlc[0].readACBlock(this.reader, ac, blkX, blkOffTop, (blkOffLeft != 0 || leftAvailable),
(blkOffLeft == 0) ? this.leftMBType : curMbType, (blkOffTop != 0 || topAvailable),
(blkOffTop == 0) ? this.topMBType[mbX] : curMbType, 0, 16, CoeffTransformer.zigzag4x4);
}
if (this.cabac.readCodedBlockFlagLumaAC(this.mDecoder, CABAC.BlockType.LUMA_16, blkX, blkOffTop, 0, this.leftMBType, this.topMBType[mbX], leftAvailable, topAvailable, this.leftCBPLuma, this.topCBPLuma[mbX], cbpLuma, curMbType) == 1)
return this.cabac.readCoeffs(this.mDecoder, CABAC.BlockType.LUMA_16, ac, 0, 16, CoeffTransformer.zigzag4x4, H264Const.identityMapping16, H264Const.identityMapping16);
return 0;
}
public void setZeroCoeff(int comp, int blkX, int blkOffTop) {
this.cavlc[comp].setZeroCoeff(blkX, blkOffTop);
}
public void savePrevCBP(int codedBlockPattern) {
if (this.activePps.entropyCodingModeFlag)
this.cabac.setPrevCBP(codedBlockPattern);
}
public int readLumaAC(boolean leftAvailable, boolean topAvailable, int mbX, MBType curMbType, int blkX, int j, int[] ac16, int blkOffLeft, int blkOffTop) {
return this.cavlc[0].readACBlock(this.reader, ac16, blkX + (j & 0x1), blkOffTop, (blkOffLeft != 0 || leftAvailable),
(blkOffLeft == 0) ? this.leftMBType : curMbType, (blkOffTop != 0 || topAvailable),
(blkOffTop == 0) ? this.topMBType[mbX] : curMbType, 0, 16, H264Const.identityMapping16);
}
public int readLumaAC8x8(int blkX, int blkY, int[] ac) {
int readCoeffs = this.cabac.readCoeffs(this.mDecoder, CABAC.BlockType.LUMA_64, ac, 0, 64, CoeffTransformer.zigzag8x8, H264Const.sig_coeff_map_8x8, H264Const.last_sig_coeff_map_8x8);
this.cabac.setCodedBlock(blkX, blkY);
this.cabac.setCodedBlock(blkX + 1, blkY);
this.cabac.setCodedBlock(blkX, blkY + 1);
this.cabac.setCodedBlock(blkX + 1, blkY + 1);
return readCoeffs;
}
public int readSubMBTypeP() {
if (!this.activePps.entropyCodingModeFlag)
return CAVLCReader.readUEtrace(this.reader, "SUB: sub_mb_type");
return this.cabac.readSubMbTypeP(this.mDecoder);
}
public int readSubMBTypeB() {
if (!this.activePps.entropyCodingModeFlag)
return CAVLCReader.readUEtrace(this.reader, "SUB: sub_mb_type");
return this.cabac.readSubMbTypeB(this.mDecoder);
}
public void readChromaDC(int mbX, boolean leftAvailable, boolean topAvailable, int[] dc, int comp, MBType curMbType) {
if (!this.activePps.entropyCodingModeFlag) {
this.cavlc[comp].readChromaDCBlock(this.reader, dc, leftAvailable, topAvailable);
} else if (this.cabac.readCodedBlockFlagChromaDC(this.mDecoder, mbX, comp, this.leftMBType, this.topMBType[mbX], leftAvailable, topAvailable, this.leftCBPChroma, this.topCBPChroma[mbX], curMbType) == 1) {
this.cabac.readCoeffs(this.mDecoder, CABAC.BlockType.CHROMA_DC, dc, 0, 4, H264Const.identityMapping16, H264Const.identityMapping16, H264Const.identityMapping16);
}
}
public void readChromaAC(boolean leftAvailable, boolean topAvailable, int mbX, int comp, MBType curMbType, int[] ac, int blkOffLeft, int blkOffTop, int blkX) {
if (!this.activePps.entropyCodingModeFlag) {
if (this.reader.remaining() <= 0)
return;
this.cavlc[comp].readACBlock(this.reader, ac, blkX, blkOffTop, (blkOffLeft != 0 || leftAvailable),
(blkOffLeft == 0) ? this.leftMBType : curMbType, (blkOffTop != 0 || topAvailable),
(blkOffTop == 0) ? this.topMBType[mbX] : curMbType, 1, 15, CoeffTransformer.zigzag4x4);
} else if (this.cabac.readCodedBlockFlagChromaAC(this.mDecoder, blkX, blkOffTop, comp, this.leftMBType, this.topMBType[mbX], leftAvailable, topAvailable, this.leftCBPChroma, this.topCBPChroma[mbX], curMbType) == 1) {
this.cabac.readCoeffs(this.mDecoder, CABAC.BlockType.CHROMA_AC, ac, 1, 15, CoeffTransformer.zigzag4x4, H264Const.identityMapping16, H264Const.identityMapping16);
}
}
public int decodeMBTypeI(int mbIdx, boolean leftAvailable, boolean topAvailable, MBType leftMBType, MBType topMBType) {
int mbType;
if (!this.activePps.entropyCodingModeFlag) {
mbType = CAVLCReader.readUEtrace(this.reader, "MB: mb_type");
} else {
mbType = this.cabac.readMBTypeI(this.mDecoder, leftMBType, topMBType, leftAvailable, topAvailable);
}
return mbType;
}
public int readMBTypeP() {
int mbType;
if (!this.activePps.entropyCodingModeFlag) {
mbType = CAVLCReader.readUEtrace(this.reader, "MB: mb_type");
} else {
mbType = this.cabac.readMBTypeP(this.mDecoder);
}
return mbType;
}
public int readMBTypeB(int mbIdx, boolean leftAvailable, boolean topAvailable, MBType leftMBType, MBType topMBType) {
int mbType;
if (!this.activePps.entropyCodingModeFlag) {
mbType = CAVLCReader.readUEtrace(this.reader, "MB: mb_type");
} else {
mbType = this.cabac.readMBTypeB(this.mDecoder, leftMBType, topMBType, leftAvailable, topAvailable);
}
return mbType;
}
public boolean readMBSkipFlag(SliceType slType, boolean leftAvailable, boolean topAvailable, int mbX) {
return this.cabac.readMBSkipFlag(this.mDecoder, slType, leftAvailable, topAvailable, mbX);
}
public void readIntra16x16(int mbType, MBlock mBlock) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
mBlock.cbp(mbType / 12 * 15, mbType / 4 % 3);
mBlock.luma16x16Mode = mbType % 4;
mBlock.chromaPredictionMode = readChromaPredMode(mbX, leftAvailable, topAvailable);
mBlock.mbQPDelta = readMBQpDelta(mBlock.prevMbType);
read16x16DC(leftAvailable, topAvailable, mbX, mBlock.dc);
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;
if ((mBlock.cbpLuma() & 1 << i >> 2) != 0) {
mBlock.nCoeff[i] = read16x16AC(leftAvailable, topAvailable, mbX, mBlock.cbpLuma(), mBlock.ac[0][i], blkOffLeft, blkOffTop, blkX, blkY);
} else if (!this.sh.pps.entropyCodingModeFlag) {
setZeroCoeff(0, blkX, blkOffTop);
}
}
if (this.chromaFormat != ColorSpace.MONO)
readChromaResidual(mBlock, leftAvailable, topAvailable, mbX);
}
public void readMBlockBDirect(MBlock mBlock) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean lAvb = this.mapper.leftAvailable(mBlock.mbIdx);
boolean tAvb = this.mapper.topAvailable(mBlock.mbIdx);
mBlock._cbp = readCodedBlockPatternInter(lAvb, tAvb, this.leftCBPLuma | this.leftCBPChroma << 4, this.topCBPLuma[mbX] | this.topCBPChroma[mbX] << 4, this.leftMBType, this.topMBType[mbX]);
mBlock.transform8x8Used = false;
if (this.transform8x8 && mBlock.cbpLuma() != 0 && this.sh.sps.direct8x8InferenceFlag)
mBlock.transform8x8Used = readTransform8x8Flag(lAvb, tAvb, this.leftMBType, this.topMBType[mbX], this.tf8x8Left, this.tf8x8Top[mbX]);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
mBlock.mbQPDelta = readMBQpDelta(mBlock.prevMbType);
readResidualLuma(mBlock, lAvb, tAvb, mbX, mbY);
readChromaResidual(mBlock, lAvb, tAvb, mbX);
this.predModeTop[mbX << 1] = this.predModeTop[(mbX << 1) + 1] = this.predModeLeft[0] = this.predModeLeft[1] = H264Const.PartPred.Direct;
}
public void readInter16x16(H264Const.PartPred p0, MBlock mBlock) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
for (int i = 0; i < 2; i++) {
if (H264Const.usesList(p0, i) && this.numRef[i] > 1)
mBlock.pb16x16.refIdx[i] = readRefIdx(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[mbX << 1], p0, mbX, 0, 0, 4, 4, i);
}
for (int list = 0; list < 2; list++)
readPredictionInter16x16(mBlock, mbX, leftAvailable, topAvailable, list, p0);
readResidualInter(mBlock, leftAvailable, topAvailable, mbX, mbY);
this.predModeLeft[0] = this.predModeLeft[1] = this.predModeTop[mbX << 1] = this.predModeTop[(mbX << 1) + 1] = p0;
}
private void readPredInter8x16(MBlock mBlock, int mbX, boolean leftAvailable, boolean topAvailable, int list, H264Const.PartPred p0, H264Const.PartPred p1) {
int blk8x8X = mbX << 1;
if (H264Const.usesList(p0, list)) {
mBlock.pb168x168.mvdX1[list] = readMVD(0, leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[blk8x8X], p0, mbX, 0, 0, 2, 4, list);
mBlock.pb168x168.mvdY1[list] = readMVD(1, leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[blk8x8X], p0, mbX, 0, 0, 2, 4, list);
}
if (H264Const.usesList(p1, list)) {
mBlock.pb168x168.mvdX2[list] = readMVD(0, true, topAvailable, MBType.P_8x16, this.topMBType[mbX], p0, this.predModeTop[blk8x8X + 1], p1, mbX, 2, 0, 2, 4, list);
mBlock.pb168x168.mvdY2[list] = readMVD(1, true, topAvailable, MBType.P_8x16, this.topMBType[mbX], p0, this.predModeTop[blk8x8X + 1], p1, mbX, 2, 0, 2, 4, list);
}
}
private void readPredictionInter16x8(MBlock mBlock, int mbX, boolean leftAvailable, boolean topAvailable, H264Const.PartPred p0, H264Const.PartPred p1, int list) {
int blk8x8X = mbX << 1;
if (H264Const.usesList(p0, list)) {
mBlock.pb168x168.mvdX1[list] = readMVD(0, leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[blk8x8X], p0, mbX, 0, 0, 4, 2, list);
mBlock.pb168x168.mvdY1[list] = readMVD(1, leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[blk8x8X], p0, mbX, 0, 0, 4, 2, list);
}
if (H264Const.usesList(p1, list)) {
mBlock.pb168x168.mvdX2[list] = readMVD(0, leftAvailable, true, this.leftMBType, MBType.P_16x8, this.predModeLeft[1], p0, p1, mbX, 0, 2, 4, 2, list);
mBlock.pb168x168.mvdY2[list] = readMVD(1, leftAvailable, true, this.leftMBType, MBType.P_16x8, this.predModeLeft[1], p0, p1, mbX, 0, 2, 4, 2, list);
}
}
public void readInter16x8(H264Const.PartPred p0, H264Const.PartPred p1, MBlock mBlock) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
for (int i = 0; i < 2; i++) {
if (H264Const.usesList(p0, i) && this.numRef[i] > 1)
mBlock.pb168x168.refIdx1[i] = readRefIdx(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[mbX << 1], p0, mbX, 0, 0, 4, 2, i);
if (H264Const.usesList(p1, i) && this.numRef[i] > 1)
mBlock.pb168x168.refIdx2[i] = readRefIdx(leftAvailable, true, this.leftMBType, mBlock.curMbType, this.predModeLeft[1], p0, p1, mbX, 0, 2, 4, 2, i);
}
for (int list = 0; list < 2; list++)
readPredictionInter16x8(mBlock, mbX, leftAvailable, topAvailable, p0, p1, list);
readResidualInter(mBlock, leftAvailable, topAvailable, mbX, mbY);
this.predModeLeft[0] = p0;
this.predModeLeft[1] = this.predModeTop[mbX << 1] = this.predModeTop[(mbX << 1) + 1] = p1;
}
public void readIntra8x16(H264Const.PartPred p0, H264Const.PartPred p1, MBlock mBlock) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
for (int i = 0; i < 2; i++) {
if (H264Const.usesList(p0, i) && this.numRef[i] > 1)
mBlock.pb168x168.refIdx1[i] = readRefIdx(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[mbX << 1], p0, mbX, 0, 0, 2, 4, i);
if (H264Const.usesList(p1, i) && this.numRef[i] > 1)
mBlock.pb168x168.refIdx2[i] = readRefIdx(true, topAvailable, mBlock.curMbType, this.topMBType[mbX], p0, this.predModeTop[(mbX << 1) + 1], p1, mbX, 2, 0, 2, 4, i);
}
for (int list = 0; list < 2; list++)
readPredInter8x16(mBlock, mbX, leftAvailable, topAvailable, list, p0, p1);
readResidualInter(mBlock, leftAvailable, topAvailable, mbX, mbY);
this.predModeTop[mbX << 1] = p0;
this.predModeTop[(mbX << 1) + 1] = this.predModeLeft[0] = this.predModeLeft[1] = p1;
}
private void readPredictionInter16x16(MBlock mBlock, int mbX, boolean leftAvailable, boolean topAvailable, int list, H264Const.PartPred curPred) {
int blk8x8X = mbX << 1;
if (H264Const.usesList(curPred, list)) {
mBlock.pb16x16.mvdX[list] = readMVD(0, leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[blk8x8X], curPred, mbX, 0, 0, 4, 4, list);
mBlock.pb16x16.mvdY[list] = readMVD(1, leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[blk8x8X], curPred, mbX, 0, 0, 4, 4, list);
}
}
private void readResidualInter(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY) {
mBlock._cbp = readCodedBlockPatternInter(leftAvailable, topAvailable, this.leftCBPLuma | this.leftCBPChroma << 4, this.topCBPLuma[mbX] | this.topCBPChroma[mbX] << 4, this.leftMBType, this.topMBType[mbX]);
mBlock.transform8x8Used = false;
if (mBlock.cbpLuma() != 0 && this.transform8x8)
mBlock.transform8x8Used = readTransform8x8Flag(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.tf8x8Left, this.tf8x8Top[mbX]);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
mBlock.mbQPDelta = readMBQpDelta(mBlock.prevMbType);
readResidualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
if (this.chromaFormat != ColorSpace.MONO)
readChromaResidual(mBlock, leftAvailable, topAvailable, mbX);
}
public void readMBlock8x8(MBlock mBlock) {
boolean noSubMBLessThen8x8;
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
if (mBlock.curMbType == MBType.P_8x8 || mBlock.curMbType == MBType.P_8x8ref0) {
readPrediction8x8P(mBlock, mbX, leftAvailable, topAvailable);
noSubMBLessThen8x8 = (mBlock.pb8x8.subMbTypes[0] == 0 && mBlock.pb8x8.subMbTypes[1] == 0 && mBlock.pb8x8.subMbTypes[2] == 0 && mBlock.pb8x8.subMbTypes[3] == 0);
} else {
readPrediction8x8B(mBlock, mbX, leftAvailable, topAvailable);
noSubMBLessThen8x8 = (H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[0]] == 0 && H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[1]] == 0 && H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[2]] == 0 && H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[3]] == 0);
}
mBlock._cbp = readCodedBlockPatternInter(leftAvailable, topAvailable, this.leftCBPLuma | this.leftCBPChroma << 4, this.topCBPLuma[mbX] | this.topCBPChroma[mbX] << 4, this.leftMBType, this.topMBType[mbX]);
mBlock.transform8x8Used = false;
if (this.transform8x8 && mBlock.cbpLuma() != 0 && noSubMBLessThen8x8)
mBlock.transform8x8Used = readTransform8x8Flag(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.tf8x8Left, this.tf8x8Top[mbX]);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
mBlock.mbQPDelta = readMBQpDelta(mBlock.prevMbType);
readResidualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
readChromaResidual(mBlock, leftAvailable, topAvailable, mbX);
}
private void readPrediction8x8P(MBlock mBlock, int mbX, boolean leftAvailable, boolean topAvailable) {
for (int i = 0; i < 4; i++)
mBlock.pb8x8.subMbTypes[i] = readSubMBTypeP();
if (this.numRef[0] > 1 && mBlock.curMbType != MBType.P_8x8ref0) {
mBlock.pb8x8.refIdx[0][0] = readRefIdx(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], H264Const.PartPred.L0, H264Const.PartPred.L0, H264Const.PartPred.L0, mbX, 0, 0, 2, 2, 0);
mBlock.pb8x8.refIdx[0][1] = readRefIdx(true, topAvailable, MBType.P_8x8, this.topMBType[mbX], H264Const.PartPred.L0, H264Const.PartPred.L0, H264Const.PartPred.L0, mbX, 2, 0, 2, 2, 0);
mBlock.pb8x8.refIdx[0][2] = readRefIdx(leftAvailable, true, this.leftMBType, MBType.P_8x8, H264Const.PartPred.L0, H264Const.PartPred.L0, H264Const.PartPred.L0, mbX, 0, 2, 2, 2, 0);
mBlock.pb8x8.refIdx[0][3] = readRefIdx(true, true, MBType.P_8x8, MBType.P_8x8, H264Const.PartPred.L0, H264Const.PartPred.L0, H264Const.PartPred.L0, mbX, 2, 2, 2, 2, 0);
}
readSubMb8x8(mBlock, 0, mBlock.pb8x8.subMbTypes[0], topAvailable, leftAvailable, 0, 0, mbX, this.leftMBType, this.topMBType[mbX], MBType.P_8x8, H264Const.PartPred.L0, H264Const.PartPred.L0, H264Const.PartPred.L0, 0);
readSubMb8x8(mBlock, 1, mBlock.pb8x8.subMbTypes[1], topAvailable, true, 2, 0, mbX, MBType.P_8x8, this.topMBType[mbX], MBType.P_8x8, H264Const.PartPred.L0, H264Const.PartPred.L0, H264Const.PartPred.L0, 0);
readSubMb8x8(mBlock, 2, mBlock.pb8x8.subMbTypes[2], true, leftAvailable, 0, 2, mbX, this.leftMBType, MBType.P_8x8, MBType.P_8x8, H264Const.PartPred.L0, H264Const.PartPred.L0, H264Const.PartPred.L0, 0);
readSubMb8x8(mBlock, 3, mBlock.pb8x8.subMbTypes[3], true, true, 2, 2, mbX, MBType.P_8x8, MBType.P_8x8, MBType.P_8x8, H264Const.PartPred.L0, H264Const.PartPred.L0, H264Const.PartPred.L0, 0);
int blk8x8X = mbX << 1;
this.predModeLeft[0] = this.predModeLeft[1] = this.predModeTop[blk8x8X] = this.predModeTop[blk8x8X + 1] = H264Const.PartPred.L0;
}
private void readPrediction8x8B(MBlock mBlock, int mbX, boolean leftAvailable, boolean topAvailable) {
H264Const.PartPred[] p = new H264Const.PartPred[4];
for (int i = 0; i < 4; i++) {
mBlock.pb8x8.subMbTypes[i] = readSubMBTypeB();
p[i] = H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[i]];
}
for (int list = 0; list < 2; list++) {
if (this.numRef[list] > 1) {
if (H264Const.usesList(p[0], list))
mBlock.pb8x8.refIdx[list][0] = readRefIdx(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.predModeLeft[0], this.predModeTop[mbX << 1], p[0], mbX, 0, 0, 2, 2, list);
if (H264Const.usesList(p[1], list))
mBlock.pb8x8.refIdx[list][1] = readRefIdx(true, topAvailable, MBType.B_8x8, this.topMBType[mbX], p[0], this.predModeTop[(mbX << 1) + 1], p[1], mbX, 2, 0, 2, 2, list);
if (H264Const.usesList(p[2], list))
mBlock.pb8x8.refIdx[list][2] = readRefIdx(leftAvailable, true, this.leftMBType, MBType.B_8x8, this.predModeLeft[1], p[0], p[2], mbX, 0, 2, 2, 2, list);
if (H264Const.usesList(p[3], list))
mBlock.pb8x8.refIdx[list][3] = readRefIdx(true, true, MBType.B_8x8, MBType.B_8x8, p[2], p[1], p[3], mbX, 2, 2, 2, 2, list);
}
}
MBlockDecoderUtils.debugPrint("Pred: " + String.valueOf(p[0]) + ", " + String.valueOf(p[1]) + ", " + String.valueOf(p[2]) + ", " + String.valueOf(p[3]));
int blk8x8X = mbX << 1;
for (int j = 0; j < 2; j++) {
if (H264Const.usesList(p[0], j))
readSubMb8x8(mBlock, 0, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[0]], topAvailable, leftAvailable, 0, 0, mbX, this.leftMBType, this.topMBType[mbX], MBType.B_8x8, this.predModeLeft[0], this.predModeTop[blk8x8X], p[0], j);
if (H264Const.usesList(p[1], j))
readSubMb8x8(mBlock, 1, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[1]], topAvailable, true, 2, 0, mbX, MBType.B_8x8, this.topMBType[mbX], MBType.B_8x8, p[0], this.predModeTop[blk8x8X + 1], p[1], j);
if (H264Const.usesList(p[2], j))
readSubMb8x8(mBlock, 2, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[2]], true, leftAvailable, 0, 2, mbX, this.leftMBType, MBType.B_8x8, MBType.B_8x8, this.predModeLeft[1], p[0], p[2], j);
if (H264Const.usesList(p[3], j))
readSubMb8x8(mBlock, 3, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[3]], true, true, 2, 2, mbX, MBType.B_8x8, MBType.B_8x8, MBType.B_8x8, p[2], p[1], p[3], j);
}
this.predModeLeft[0] = p[1];
this.predModeTop[blk8x8X] = p[2];
this.predModeLeft[1] = this.predModeTop[blk8x8X + 1] = p[3];
}
private void readSubMb8x8(MBlock mBlock, int partNo, int subMbType, boolean tAvb, boolean lAvb, int blk8x8X, int blk8x8Y, int mbX, MBType leftMBType, MBType topMBType, MBType curMBType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred partPred, int list) {
switch (subMbType) {
case 3:
readSub4x4(mBlock, partNo, tAvb, lAvb, blk8x8X, blk8x8Y, mbX, leftMBType, topMBType, curMBType, leftPred, topPred, partPred, list);
break;
case 2:
readSub4x8(mBlock, partNo, tAvb, lAvb, blk8x8X, blk8x8Y, mbX, leftMBType, topMBType, curMBType, leftPred, topPred, partPred, list);
break;
case 1:
readSub8x4(mBlock, partNo, tAvb, lAvb, blk8x8X, blk8x8Y, mbX, leftMBType, topMBType, curMBType, leftPred, topPred, partPred, list);
break;
case 0:
readSub8x8(mBlock, partNo, tAvb, lAvb, blk8x8X, blk8x8Y, mbX, leftMBType, topMBType, leftPred, topPred, partPred, list);
break;
}
}
private void readSub8x8(MBlock mBlock, int partNo, boolean tAvb, boolean lAvb, int blk8x8X, int blk8x8Y, int mbX, MBType leftMBType, MBType topMBType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred partPred, int list) {
mBlock.pb8x8.mvdX1[list][partNo] = readMVD(0, lAvb, tAvb, leftMBType, topMBType, leftPred, topPred, partPred, mbX, blk8x8X, blk8x8Y, 2, 2, list);
mBlock.pb8x8.mvdY1[list][partNo] = readMVD(1, lAvb, tAvb, leftMBType, topMBType, leftPred, topPred, partPred, mbX, blk8x8X, blk8x8Y, 2, 2, list);
MBlockDecoderUtils.debugPrint("mvd: (%d, %d)", mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo]);
}
private void readSub8x4(MBlock mBlock, int partNo, boolean tAvb, boolean lAvb, int blk8x8X, int blk8x8Y, int mbX, MBType leftMBType, MBType topMBType, MBType curMBType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred partPred, int list) {
mBlock.pb8x8.mvdX1[list][partNo] = readMVD(0, lAvb, tAvb, leftMBType, topMBType, leftPred, topPred, partPred, mbX, blk8x8X, blk8x8Y, 2, 1, list);
mBlock.pb8x8.mvdY1[list][partNo] = readMVD(1, lAvb, tAvb, leftMBType, topMBType, leftPred, topPred, partPred, mbX, blk8x8X, blk8x8Y, 2, 1, list);
mBlock.pb8x8.mvdX2[list][partNo] = readMVD(0, lAvb, true, leftMBType, curMBType, leftPred, partPred, partPred, mbX, blk8x8X, blk8x8Y + 1, 2, 1, list);
mBlock.pb8x8.mvdY2[list][partNo] = readMVD(1, lAvb, true, leftMBType, curMBType, leftPred, partPred, partPred, mbX, blk8x8X, blk8x8Y + 1, 2, 1, list);
}
private void readSub4x8(MBlock mBlock, int partNo, boolean tAvb, boolean lAvb, int blk8x8X, int blk8x8Y, int mbX, MBType leftMBType, MBType topMBType, MBType curMBType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred partPred, int list) {
mBlock.pb8x8.mvdX1[list][partNo] = readMVD(0, lAvb, tAvb, leftMBType, topMBType, leftPred, topPred, partPred, mbX, blk8x8X, blk8x8Y, 1, 2, list);
mBlock.pb8x8.mvdY1[list][partNo] = readMVD(1, lAvb, tAvb, leftMBType, topMBType, leftPred, topPred, partPred, mbX, blk8x8X, blk8x8Y, 1, 2, list);
mBlock.pb8x8.mvdX2[list][partNo] = readMVD(0, true, tAvb, curMBType, topMBType, partPred, topPred, partPred, mbX, blk8x8X + 1, blk8x8Y, 1, 2, list);
mBlock.pb8x8.mvdY2[list][partNo] = readMVD(1, true, tAvb, curMBType, topMBType, partPred, topPred, partPred, mbX, blk8x8X + 1, blk8x8Y, 1, 2, list);
}
private void readSub4x4(MBlock mBlock, int partNo, boolean tAvb, boolean lAvb, int blk8x8X, int blk8x8Y, int mbX, MBType leftMBType, MBType topMBType, MBType curMBType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred partPred, int list) {
mBlock.pb8x8.mvdX1[list][partNo] = readMVD(0, lAvb, tAvb, leftMBType, topMBType, leftPred, topPred, partPred, mbX, blk8x8X, blk8x8Y, 1, 1, list);
mBlock.pb8x8.mvdY1[list][partNo] = readMVD(1, lAvb, tAvb, leftMBType, topMBType, leftPred, topPred, partPred, mbX, blk8x8X, blk8x8Y, 1, 1, list);
mBlock.pb8x8.mvdX2[list][partNo] = readMVD(0, true, tAvb, curMBType, topMBType, partPred, topPred, partPred, mbX, blk8x8X + 1, blk8x8Y, 1, 1, list);
mBlock.pb8x8.mvdY2[list][partNo] = readMVD(1, true, tAvb, curMBType, topMBType, partPred, topPred, partPred, mbX, blk8x8X + 1, blk8x8Y, 1, 1, list);
mBlock.pb8x8.mvdX3[list][partNo] = readMVD(0, lAvb, true, leftMBType, curMBType, leftPred, partPred, partPred, mbX, blk8x8X, blk8x8Y + 1, 1, 1, list);
mBlock.pb8x8.mvdY3[list][partNo] = readMVD(1, lAvb, true, leftMBType, curMBType, leftPred, partPred, partPred, mbX, blk8x8X, blk8x8Y + 1, 1, 1, list);
mBlock.pb8x8.mvdX4[list][partNo] = readMVD(0, true, true, curMBType, curMBType, partPred, partPred, partPred, mbX, blk8x8X + 1, blk8x8Y + 1, 1, 1, list);
mBlock.pb8x8.mvdY4[list][partNo] = readMVD(1, true, true, curMBType, curMBType, partPred, partPred, partPred, mbX, blk8x8X + 1, blk8x8Y + 1, 1, 1, list);
}
public void readIntraNxN(MBlock mBlock) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
mBlock.transform8x8Used = false;
if (this.transform8x8)
mBlock.transform8x8Used = readTransform8x8Flag(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], this.tf8x8Left, this.tf8x8Top[mbX]);
if (!mBlock.transform8x8Used) {
for (int i = 0; i < 16; i++) {
int blkX = H264Const.MB_BLK_OFF_LEFT[i];
int blkY = H264Const.MB_BLK_OFF_TOP[i];
mBlock.lumaModes[i] = readPredictionI4x4Block(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], blkX, blkY, mbX);
}
} else {
for (int i = 0; i < 4; i++) {
int blkX = (i & 0x1) << 1;
int blkY = i & 0x2;
mBlock.lumaModes[i] = readPredictionI4x4Block(leftAvailable, topAvailable, this.leftMBType, this.topMBType[mbX], blkX, blkY, mbX);
this.i4x4PredLeft[blkY + 1] = this.i4x4PredLeft[blkY];
this.i4x4PredTop[(mbX << 2) + blkX + 1] = this.i4x4PredTop[(mbX << 2) + blkX];
}
}
mBlock.chromaPredictionMode = readChromaPredMode(mbX, leftAvailable, topAvailable);
mBlock._cbp = readCodedBlockPatternIntra(leftAvailable, topAvailable, this.leftCBPLuma | this.leftCBPChroma << 4, this.topCBPLuma[mbX] | this.topCBPChroma[mbX] << 4, this.leftMBType, this.topMBType[mbX]);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
mBlock.mbQPDelta = readMBQpDelta(mBlock.prevMbType);
readResidualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
if (this.chromaFormat != ColorSpace.MONO)
readChromaResidual(mBlock, leftAvailable, topAvailable, mbX);
}
public void readResidualLuma(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY) {
if (!mBlock.transform8x8Used) {
readLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
} else if (this.sh.pps.entropyCodingModeFlag) {
readLuma8x8CABAC(mBlock, mbX, mbY);
} else {
readLuma8x8CAVLC(mBlock, leftAvailable, topAvailable, mbX, mbY);
}
}
private void readLuma(MBlock mBlock, boolean leftAvailable, boolean topAvailable, 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;
if ((mBlock.cbpLuma() & 1 << i >> 2) == 0) {
if (!this.sh.pps.entropyCodingModeFlag)
setZeroCoeff(0, blkX, blkOffTop);
} else {
mBlock.nCoeff[i] = readResidualAC(leftAvailable, topAvailable, mbX, mBlock.curMbType, mBlock.cbpLuma(), blkOffLeft, blkOffTop, blkX, blkY, mBlock.ac[0][i]);
}
}
savePrevCBP(mBlock._cbp);
}
private void readLuma8x8CABAC(MBlock mBlock, int mbX, int mbY) {
for (int i = 0; i < 4; i++) {
int blkOffLeft = (i & 0x1) << 1;
int blkOffTop = i & 0x2;
int blkX = (mbX << 2) + blkOffLeft;
int blkY = (mbY << 2) + blkOffTop;
if ((mBlock.cbpLuma() & 1 << i) != 0) {
int nCoeff = readLumaAC8x8(blkX, blkY, mBlock.ac[0][i]);
int blk4x4Offset = i << 2;
mBlock.nCoeff[blk4x4Offset + 3] = nCoeff;
mBlock.nCoeff[blk4x4Offset + 2] = nCoeff;
mBlock.nCoeff[blk4x4Offset + 1] = nCoeff;
mBlock.nCoeff[blk4x4Offset] = nCoeff;
}
}
savePrevCBP(mBlock._cbp);
}
private void readLuma8x8CAVLC(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY) {
for (int i = 0; i < 4; i++) {
int blk8x8OffLeft = (i & 0x1) << 1;
int blk8x8OffTop = i & 0x2;
int blkX = (mbX << 2) + blk8x8OffLeft;
int blkY = (mbY << 2) + blk8x8OffTop;
if ((mBlock.cbpLuma() & 1 << i) == 0) {
setZeroCoeff(0, blkX, blk8x8OffTop);
setZeroCoeff(0, blkX + 1, blk8x8OffTop);
setZeroCoeff(0, blkX, blk8x8OffTop + 1);
setZeroCoeff(0, blkX + 1, blk8x8OffTop + 1);
} else {
int coeffs = 0;
for (int j = 0; j < 4; j++) {
int[] ac16 = new int[16];
int blkOffLeft = blk8x8OffLeft + (j & 0x1);
int blkOffTop = blk8x8OffTop + (j >> 1);
coeffs += readLumaAC(leftAvailable, topAvailable, mbX, mBlock.curMbType, blkX, j, ac16, blkOffLeft, blkOffTop);
for (int k = 0; k < 16; k++)
mBlock.ac[0][i][CoeffTransformer.zigzag8x8[(k << 2) + j]] = ac16[k];
}
int blk4x4Offset = i << 2;
mBlock.nCoeff[blk4x4Offset + 3] = coeffs;
mBlock.nCoeff[blk4x4Offset + 2] = coeffs;
mBlock.nCoeff[blk4x4Offset + 1] = coeffs;
mBlock.nCoeff[blk4x4Offset] = coeffs;
}
}
}
public void readChromaResidual(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX) {
if (mBlock.cbpChroma() != 0) {
if ((mBlock.cbpChroma() & 0x3) > 0) {
readChromaDC(mbX, leftAvailable, topAvailable, mBlock.dc1, 1, mBlock.curMbType);
readChromaDC(mbX, leftAvailable, topAvailable, mBlock.dc2, 2, mBlock.curMbType);
}
_readChromaAC(leftAvailable, topAvailable, mbX, mBlock.dc1, 1, mBlock.curMbType,
((mBlock.cbpChroma() & 0x2) > 0), mBlock.ac[1]);
_readChromaAC(leftAvailable, topAvailable, mbX, mBlock.dc2, 2, mBlock.curMbType,
((mBlock.cbpChroma() & 0x2) > 0), mBlock.ac[2]);
} else if (!this.sh.pps.entropyCodingModeFlag) {
setZeroCoeff(1, mbX << 1, 0);
setZeroCoeff(1, (mbX << 1) + 1, 1);
setZeroCoeff(2, mbX << 1, 0);
setZeroCoeff(2, (mbX << 1) + 1, 1);
}
}
private void _readChromaAC(boolean leftAvailable, boolean topAvailable, int mbX, int[] dc, int comp, MBType curMbType, boolean codedAC, int[][] residualOut) {
for (int i = 0; i < dc.length; i++) {
int[] ac = residualOut[i];
int blkOffLeft = H264Const.MB_BLK_OFF_LEFT[i];
int blkOffTop = H264Const.MB_BLK_OFF_TOP[i];
int blkX = (mbX << 1) + blkOffLeft;
if (codedAC) {
readChromaAC(leftAvailable, topAvailable, mbX, comp, curMbType, ac, blkOffLeft, blkOffTop, blkX);
} else if (!this.sh.pps.entropyCodingModeFlag) {
setZeroCoeff(comp, blkX, blkOffTop);
}
}
}
private void readIPCM(MBlock mBlock) {
this.reader.align();
for (int i = 0; i < 256; i++)
mBlock.ipcm.samplesLuma[i] = this.reader.readNBit(8);
int MbWidthC = 16 >> this.chromaFormat.compWidth[1];
int MbHeightC = 16 >> this.chromaFormat.compHeight[1];
for (int j = 0; j < 2 * MbWidthC * MbHeightC; j++)
mBlock.ipcm.samplesChroma[j] = this.reader.readNBit(8);
}
public void readMBlock(MBlock mBlock, SliceType sliceType) {
if (sliceType == SliceType.I) {
readMBlockI(mBlock);
} else if (sliceType == SliceType.P) {
readMBlockP(mBlock);
} else {
readMBlockB(mBlock);
}
int mbX = this.mapper.getMbX(mBlock.mbIdx);
this.topCBPLuma[mbX] = this.leftCBPLuma = mBlock.cbpLuma();
this.topCBPChroma[mbX] = this.leftCBPChroma = mBlock.cbpChroma();
this.tf8x8Left = this.tf8x8Top[mbX] = mBlock.transform8x8Used;
}
private void readMBlockI(MBlock mBlock) {
mBlock.mbType = decodeMBTypeI(mBlock.mbIdx, this.mapper.leftAvailable(mBlock.mbIdx),
this.mapper.topAvailable(mBlock.mbIdx), this.leftMBType, this.topMBType[this.mapper.getMbX(mBlock.mbIdx)]);
readMBlockIInt(mBlock, mBlock.mbType);
}
private void readMBlockIInt(MBlock mBlock, int mbType) {
if (mbType == 0) {
mBlock.curMbType = MBType.I_NxN;
readIntraNxN(mBlock);
} else if (mbType >= 1 && mbType <= 24) {
mBlock.curMbType = MBType.I_16x16;
readIntra16x16(mbType - 1, mBlock);
} else {
Logger.warn("IPCM macroblock found. Not tested, may cause unpredictable behavior.");
mBlock.curMbType = MBType.I_PCM;
readIPCM(mBlock);
}
}
private void readMBlockP(MBlock mBlock) {
mBlock.mbType = readMBTypeP();
switch (mBlock.mbType) {
case 0:
mBlock.curMbType = MBType.P_16x16;
readInter16x16(H264Const.PartPred.L0, mBlock);
break;
case 1:
mBlock.curMbType = MBType.P_16x8;
readInter16x8(H264Const.PartPred.L0, H264Const.PartPred.L0, mBlock);
break;
case 2:
mBlock.curMbType = MBType.P_8x16;
readIntra8x16(H264Const.PartPred.L0, H264Const.PartPred.L0, mBlock);
break;
case 3:
mBlock.curMbType = MBType.P_8x8;
readMBlock8x8(mBlock);
break;
case 4:
mBlock.curMbType = MBType.P_8x8ref0;
readMBlock8x8(mBlock);
break;
default:
readMBlockIInt(mBlock, mBlock.mbType - 5);
break;
}
}
private void readMBlockB(MBlock mBlock) {
mBlock.mbType = readMBTypeB(mBlock.mbIdx, this.mapper.leftAvailable(mBlock.mbIdx),
this.mapper.topAvailable(mBlock.mbIdx), this.leftMBType, this.topMBType[this.mapper.getMbX(mBlock.mbIdx)]);
if (mBlock.mbType >= 23) {
readMBlockIInt(mBlock, mBlock.mbType - 23);
} else {
mBlock.curMbType = H264Const.bMbTypes[mBlock.mbType];
if (mBlock.mbType == 0) {
readMBlockBDirect(mBlock);
} else if (mBlock.mbType <= 3) {
readInter16x16(H264Const.bPredModes[mBlock.mbType][0], mBlock);
} else if (mBlock.mbType == 22) {
readMBlock8x8(mBlock);
} else if ((mBlock.mbType & 0x1) == 0) {
readInter16x8(H264Const.bPredModes[mBlock.mbType][0], H264Const.bPredModes[mBlock.mbType][1], mBlock);
} else {
readIntra8x16(H264Const.bPredModes[mBlock.mbType][0], H264Const.bPredModes[mBlock.mbType][1], mBlock);
}
}
}
public SliceHeader getSliceHeader() {
return this.sh;
}
public NALUnit getNALUnit() {
return this.nalUnit;
}
}

View file

@ -0,0 +1,47 @@
package org.jcodec.codecs.h264.decode.aso;
public class FlatMBlockMapper implements Mapper {
private int frameWidthInMbs;
private int firstMBAddr;
public FlatMBlockMapper(int frameWidthInMbs, int firstMBAddr) {
this.frameWidthInMbs = frameWidthInMbs;
this.firstMBAddr = firstMBAddr;
}
public boolean leftAvailable(int index) {
int mbAddr = index + this.firstMBAddr;
boolean atTheBorder = (mbAddr % this.frameWidthInMbs == 0);
return (!atTheBorder && mbAddr > this.firstMBAddr);
}
public boolean topAvailable(int index) {
int mbAddr = index + this.firstMBAddr;
return (mbAddr - this.frameWidthInMbs >= this.firstMBAddr);
}
public int getAddress(int index) {
return this.firstMBAddr + index;
}
public int getMbX(int index) {
return getAddress(index) % this.frameWidthInMbs;
}
public int getMbY(int index) {
return getAddress(index) / this.frameWidthInMbs;
}
public boolean topRightAvailable(int index) {
int mbAddr = index + this.firstMBAddr;
boolean atTheBorder = ((mbAddr + 1) % this.frameWidthInMbs == 0);
return (!atTheBorder && mbAddr - this.frameWidthInMbs + 1 >= this.firstMBAddr);
}
public boolean topLeftAvailable(int index) {
int mbAddr = index + this.firstMBAddr;
boolean atTheBorder = (mbAddr % this.frameWidthInMbs == 0);
return (!atTheBorder && mbAddr - this.frameWidthInMbs - 1 >= this.firstMBAddr);
}
}

View file

@ -0,0 +1,27 @@
package org.jcodec.codecs.h264.decode.aso;
public class MBToSliceGroupMap {
private int[] groups;
private int[] indices;
private int[][] inverse;
public MBToSliceGroupMap(int[] groups, int[] indices, int[][] inverse) {
this.groups = groups;
this.indices = indices;
this.inverse = inverse;
}
public int[] getGroups() {
return this.groups;
}
public int[] getIndices() {
return this.indices;
}
public int[][] getInverse() {
return this.inverse;
}
}

View file

@ -0,0 +1,101 @@
package org.jcodec.codecs.h264.decode.aso;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
public class MapManager {
private SeqParameterSet sps;
private PictureParameterSet pps;
private MBToSliceGroupMap mbToSliceGroupMap;
private int prevSliceGroupChangeCycle;
public MapManager(SeqParameterSet sps, PictureParameterSet pps) {
this.sps = sps;
this.pps = pps;
this.mbToSliceGroupMap = buildMap(sps, pps);
}
private MBToSliceGroupMap buildMap(SeqParameterSet sps, PictureParameterSet pps) {
int numGroups = pps.numSliceGroupsMinus1 + 1;
if (numGroups > 1) {
int[] map;
int picWidthInMbs = sps.picWidthInMbsMinus1 + 1;
int picHeightInMbs = SeqParameterSet.getPicHeightInMbs(sps);
if (pps.sliceGroupMapType == 0) {
int[] runLength = new int[numGroups];
for (int i = 0; i < numGroups; i++)
runLength[i] = pps.runLengthMinus1[i] + 1;
map = SliceGroupMapBuilder.buildInterleavedMap(picWidthInMbs, picHeightInMbs, runLength);
} else if (pps.sliceGroupMapType == 1) {
map = SliceGroupMapBuilder.buildDispersedMap(picWidthInMbs, picHeightInMbs, numGroups);
} else if (pps.sliceGroupMapType == 2) {
map = SliceGroupMapBuilder.buildForegroundMap(picWidthInMbs, picHeightInMbs, numGroups, pps.topLeft, pps.bottomRight);
} else {
if (pps.sliceGroupMapType >= 3 && pps.sliceGroupMapType <= 5)
return null;
if (pps.sliceGroupMapType == 6) {
map = pps.sliceGroupId;
} else {
throw new RuntimeException("Unsupported slice group map type");
}
}
return buildMapIndices(map, numGroups);
}
return null;
}
private MBToSliceGroupMap buildMapIndices(int[] map, int numGroups) {
int[] ind = new int[numGroups];
int[] indices = new int[map.length];
for (int i = 0; i < map.length; i++) {
ind[map[i]] = ind[map[i]] + 1;
indices[i] = ind[map[i]];
}
int[][] inverse = new int[numGroups][];
for (int k = 0; k < numGroups; k++)
inverse[k] = new int[ind[k]];
ind = new int[numGroups];
for (int j = 0; j < map.length; j++) {
int sliceGroup = map[j];
ind[sliceGroup] = ind[sliceGroup] + 1;
inverse[sliceGroup][ind[sliceGroup]] = j;
}
return new MBToSliceGroupMap(map, indices, inverse);
}
private void updateMap(SliceHeader sh) {
int mapType = this.pps.sliceGroupMapType;
int numGroups = this.pps.numSliceGroupsMinus1 + 1;
if (numGroups > 1 && mapType >= 3 && mapType <= 5 && (sh.sliceGroupChangeCycle != this.prevSliceGroupChangeCycle || this.mbToSliceGroupMap == null)) {
int[] map;
this.prevSliceGroupChangeCycle = sh.sliceGroupChangeCycle;
int picWidthInMbs = this.sps.picWidthInMbsMinus1 + 1;
int picHeightInMbs = SeqParameterSet.getPicHeightInMbs(this.sps);
int picSizeInMapUnits = picWidthInMbs * picHeightInMbs;
int mapUnitsInSliceGroup0 = sh.sliceGroupChangeCycle * (this.pps.sliceGroupChangeRateMinus1 + 1);
mapUnitsInSliceGroup0 = (mapUnitsInSliceGroup0 > picSizeInMapUnits) ? picSizeInMapUnits : mapUnitsInSliceGroup0;
int sizeOfUpperLeftGroup = this.pps.sliceGroupChangeDirectionFlag ? (picSizeInMapUnits - mapUnitsInSliceGroup0) :
mapUnitsInSliceGroup0;
if (mapType == 3) {
map = SliceGroupMapBuilder.buildBoxOutMap(picWidthInMbs, picHeightInMbs, this.pps.sliceGroupChangeDirectionFlag, mapUnitsInSliceGroup0);
} else if (mapType == 4) {
map = SliceGroupMapBuilder.buildRasterScanMap(picWidthInMbs, picHeightInMbs, sizeOfUpperLeftGroup, this.pps.sliceGroupChangeDirectionFlag);
} else {
map = SliceGroupMapBuilder.buildWipeMap(picWidthInMbs, picHeightInMbs, sizeOfUpperLeftGroup, this.pps.sliceGroupChangeDirectionFlag);
}
this.mbToSliceGroupMap = buildMapIndices(map, numGroups);
}
}
public Mapper getMapper(SliceHeader sh) {
updateMap(sh);
int firstMBInSlice = sh.firstMbInSlice;
if (this.pps.numSliceGroupsMinus1 > 0)
return new PrebuiltMBlockMapper(this.mbToSliceGroupMap, firstMBInSlice, this.sps.picWidthInMbsMinus1 + 1);
return new FlatMBlockMapper(this.sps.picWidthInMbsMinus1 + 1, firstMBInSlice);
}
}

View file

@ -0,0 +1,17 @@
package org.jcodec.codecs.h264.decode.aso;
public interface Mapper {
boolean leftAvailable(int paramInt);
boolean topAvailable(int paramInt);
int getAddress(int paramInt);
int getMbX(int paramInt);
int getMbY(int paramInt);
boolean topRightAvailable(int paramInt);
boolean topLeftAvailable(int paramInt);
}

View file

@ -0,0 +1,57 @@
package org.jcodec.codecs.h264.decode.aso;
public class PrebuiltMBlockMapper implements Mapper {
private MBToSliceGroupMap map;
private int firstMBInSlice;
private int groupId;
private int picWidthInMbs;
private int indexOfFirstMb;
public PrebuiltMBlockMapper(MBToSliceGroupMap map, int firstMBInSlice, int picWidthInMbs) {
this.map = map;
this.firstMBInSlice = firstMBInSlice;
this.groupId = map.getGroups()[firstMBInSlice];
this.picWidthInMbs = picWidthInMbs;
this.indexOfFirstMb = map.getIndices()[firstMBInSlice];
}
public int getAddress(int mbIndex) {
return this.map.getInverse()[this.groupId][mbIndex + this.indexOfFirstMb];
}
public boolean leftAvailable(int mbIndex) {
int mbAddr = this.map.getInverse()[this.groupId][mbIndex + this.indexOfFirstMb];
int leftMBAddr = mbAddr - 1;
return (leftMBAddr >= this.firstMBInSlice && mbAddr % this.picWidthInMbs != 0 && this.map.getGroups()[leftMBAddr] == this.groupId);
}
public boolean topAvailable(int mbIndex) {
int mbAddr = this.map.getInverse()[this.groupId][mbIndex + this.indexOfFirstMb];
int topMBAddr = mbAddr - this.picWidthInMbs;
return (topMBAddr >= this.firstMBInSlice && this.map.getGroups()[topMBAddr] == this.groupId);
}
public int getMbX(int index) {
return getAddress(index) % this.picWidthInMbs;
}
public int getMbY(int index) {
return getAddress(index) / this.picWidthInMbs;
}
public boolean topRightAvailable(int mbIndex) {
int mbAddr = this.map.getInverse()[this.groupId][mbIndex + this.indexOfFirstMb];
int topRMBAddr = mbAddr - this.picWidthInMbs + 1;
return (topRMBAddr >= this.firstMBInSlice && (mbAddr + 1) % this.picWidthInMbs != 0 && this.map.getGroups()[topRMBAddr] == this.groupId);
}
public boolean topLeftAvailable(int mbIndex) {
int mbAddr = this.map.getInverse()[this.groupId][mbIndex + this.indexOfFirstMb];
int topLMBAddr = mbAddr - this.picWidthInMbs - 1;
return (topLMBAddr >= this.firstMBInSlice && mbAddr % this.picWidthInMbs != 0 && this.map.getGroups()[topLMBAddr] == this.groupId);
}
}

View file

@ -0,0 +1,137 @@
package org.jcodec.codecs.h264.decode.aso;
public class SliceGroupMapBuilder {
public static int[] buildInterleavedMap(int picWidthInMbs, int picHeightInMbs, int[] runLength) {
int numSliceGroups = runLength.length;
int picSizeInMbs = picWidthInMbs * picHeightInMbs;
int[] groups = new int[picSizeInMbs];
int i = 0;
do {
for (int iGroup = 0; iGroup < numSliceGroups && i < picSizeInMbs; i += runLength[iGroup++]) {
for (int j = 0; j < runLength[iGroup] && i + j < picSizeInMbs; j++)
groups[i + j] = iGroup;
}
} while (i < picSizeInMbs);
return groups;
}
public static int[] buildDispersedMap(int picWidthInMbs, int picHeightInMbs, int numSliceGroups) {
int picSizeInMbs = picWidthInMbs * picHeightInMbs;
int[] groups = new int[picSizeInMbs];
for (int i = 0; i < picSizeInMbs; i++) {
int group = (i % picWidthInMbs + i / picWidthInMbs * numSliceGroups / 2) % numSliceGroups;
groups[i] = group;
}
return groups;
}
public static int[] buildForegroundMap(int picWidthInMbs, int picHeightInMbs, int numSliceGroups, int[] topLeftAddr, int[] bottomRightAddr) {
int picSizeInMbs = picWidthInMbs * picHeightInMbs;
int[] groups = new int[picSizeInMbs];
for (int i = 0; i < picSizeInMbs; i++)
groups[i] = numSliceGroups - 1;
int tot = 0;
for (int iGroup = numSliceGroups - 2; iGroup >= 0; iGroup--) {
int yTopLeft = topLeftAddr[iGroup] / picWidthInMbs;
int xTopLeft = topLeftAddr[iGroup] % picWidthInMbs;
int yBottomRight = bottomRightAddr[iGroup] / picWidthInMbs;
int xBottomRight = bottomRightAddr[iGroup] % picWidthInMbs;
int sz = (yBottomRight - yTopLeft + 1) * (xBottomRight - xTopLeft + 1);
tot += sz;
int ind = 0;
for (int y = yTopLeft; y <= yBottomRight; y++) {
for (int x = xTopLeft; x <= xBottomRight; x++) {
int mbAddr = y * picWidthInMbs + x;
groups[mbAddr] = iGroup;
}
}
}
return groups;
}
public static int[] buildBoxOutMap(int picWidthInMbs, int picHeightInMbs, boolean changeDirection, int numberOfMbsInBox) {
int picSizeInMbs = picWidthInMbs * picHeightInMbs;
int[] groups = new int[picSizeInMbs];
int changeDirectionInt = changeDirection ? 1 : 0;
for (int i = 0; i < picSizeInMbs; i++)
groups[i] = 1;
int x = (picWidthInMbs - changeDirectionInt) / 2;
int y = (picHeightInMbs - changeDirectionInt) / 2;
int leftBound = x;
int topBound = y;
int rightBound = x;
int bottomBound = y;
int xDir = changeDirectionInt - 1;
int yDir = changeDirectionInt;
boolean mapUnitVacant = false;
for (int k = 0; k < numberOfMbsInBox; k += mapUnitVacant ? 1 : 0) {
int mbAddr = y * picWidthInMbs + x;
mapUnitVacant = (groups[mbAddr] == 1);
if (mapUnitVacant)
groups[mbAddr] = 0;
if (xDir == -1 && x == leftBound) {
leftBound = Max(leftBound - 1, 0);
x = leftBound;
xDir = 0;
yDir = 2 * changeDirectionInt - 1;
} else if (xDir == 1 && x == rightBound) {
rightBound = Min(rightBound + 1, picWidthInMbs - 1);
x = rightBound;
xDir = 0;
yDir = 1 - 2 * changeDirectionInt;
} else if (yDir == -1 && y == topBound) {
topBound = Max(topBound - 1, 0);
y = topBound;
xDir = 1 - 2 * changeDirectionInt;
yDir = 0;
} else if (yDir == 1 && y == bottomBound) {
bottomBound = Min(bottomBound + 1, picHeightInMbs - 1);
y = bottomBound;
xDir = 2 * changeDirectionInt - 1;
yDir = 0;
} else {
x += xDir;
y += yDir;
}
}
return groups;
}
private static int Min(int i, int j) {
return (i < j) ? i : j;
}
private static int Max(int i, int j) {
return (i > j) ? i : j;
}
public static int[] buildRasterScanMap(int picWidthInMbs, int picHeightInMbs, int sizeOfUpperLeftGroup, boolean changeDirection) {
int picSizeInMbs = picWidthInMbs * picHeightInMbs;
int[] groups = new int[picSizeInMbs];
int changeDirectionInt = changeDirection ? 1 : 0;
int i;
for (i = 0; i < sizeOfUpperLeftGroup; i++)
groups[i] = changeDirectionInt;
for (; i < picSizeInMbs; i++)
groups[i] = 1 - changeDirectionInt;
return groups;
}
public static int[] buildWipeMap(int picWidthInMbs, int picHeightInMbs, int sizeOfUpperLeftGroup, boolean changeDirection) {
int picSizeInMbs = picWidthInMbs * picHeightInMbs;
int[] groups = new int[picSizeInMbs];
int changeDirectionInt = changeDirection ? 1 : 0;
int k = 0;
for (int j = 0; j < picWidthInMbs; j++) {
for (int i = 0; i < picHeightInMbs; i++) {
int mbAddr = i * picWidthInMbs + j;
if (k++ < sizeOfUpperLeftGroup) {
groups[mbAddr] = changeDirectionInt;
} else {
groups[mbAddr] = 1 - changeDirectionInt;
}
}
}
return groups;
}
}

View file

@ -0,0 +1,371 @@
package org.jcodec.codecs.h264.decode.deblock;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.DeblockerInput;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class DeblockingFilter {
public static int[] alphaTab = new int[] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 4, 4, 5, 6,
7, 8, 9, 10, 12, 13, 15, 17, 20, 22,
25, 28, 32, 36, 40, 45, 50, 56, 63, 71,
80, 90, 101, 113, 127, 144, 162, 182, 203, 226,
255, 255 };
public static int[] betaTab = new int[] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 2, 3,
3, 3, 3, 4, 4, 4, 6, 6, 7, 7,
8, 8, 9, 9, 10, 10, 11, 11, 12, 12,
13, 13, 14, 14, 15, 15, 16, 16, 17, 17,
18, 18 };
public static int[][] tcs = new int[][] { new int[] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 2, 2, 2, 3, 3, 3,
4, 4, 4, 5, 6, 6, 7, 8, 9, 10,
11, 13 }, new int[] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 3, 3, 3, 4, 4,
5, 5, 6, 7, 8, 8, 10, 11, 12, 13,
15, 17 }, new int[] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
2, 3, 3, 3, 4, 4, 4, 5, 6, 6,
7, 8, 9, 10, 11, 13, 14, 16, 18, 20,
23, 25 } };
private DeblockerInput di;
public DeblockingFilter(int bitDepthLuma, int bitDepthChroma, DeblockerInput di) {
this.di = di;
}
public void deblockFrame(Picture result) {
ColorSpace color = result.getColor();
int[][] bsV = new int[4][4], bsH = new int[4][4];
for (int i = 0; i < this.di.shs.length; i++) {
calcBsH(result, i, bsH);
calcBsV(result, i, bsV);
for (int c = 0; c < color.nComp; c++) {
fillVerticalEdge(result, c, i, bsV);
fillHorizontalEdge(result, c, i, bsH);
}
}
}
static int[] inverse = new int[] {
0, 1, 4, 5, 2, 3, 6, 7, 8, 9,
12, 13, 10, 11, 14, 15 };
private int calcBoundaryStrenth(boolean atMbBoundary, boolean leftIntra, boolean rightIntra, int leftCoeff, int rightCoeff, int mvA0, int mvB0, int mvA1, int mvB1, int mbAddrA, int mbAddrB) {
if (atMbBoundary && (leftIntra || rightIntra))
return 4;
if (leftIntra || rightIntra)
return 3;
if (leftCoeff > 0 || rightCoeff > 0)
return 2;
int nA = ((H264Utils.Mv.mvRef(mvA0) == -1) ? 0 : 1) + ((H264Utils.Mv.mvRef(mvA1) == -1) ? 0 : 1);
int nB = ((H264Utils.Mv.mvRef(mvB0) == -1) ? 0 : 1) + ((H264Utils.Mv.mvRef(mvB1) == -1) ? 0 : 1);
if (nA != nB)
return 1;
Picture ra0 = (H264Utils.Mv.mvRef(mvA0) < 0) ? null : this.di.refsUsed[mbAddrA][0][H264Utils.Mv.mvRef(mvA0)];
Picture ra1 = (H264Utils.Mv.mvRef(mvA1) < 0) ? null : this.di.refsUsed[mbAddrA][1][H264Utils.Mv.mvRef(mvA1)];
Picture rb0 = (H264Utils.Mv.mvRef(mvB0) < 0) ? null : this.di.refsUsed[mbAddrB][0][H264Utils.Mv.mvRef(mvB0)];
Picture rb1 = (H264Utils.Mv.mvRef(mvB1) < 0) ? null : this.di.refsUsed[mbAddrB][1][H264Utils.Mv.mvRef(mvB1)];
if ((ra0 != rb0 && ra0 != rb1) || (ra1 != rb0 && ra1 != rb1) || (rb0 != ra0 && rb0 != ra1) || (rb1 != ra0 && rb1 != ra1))
return 1;
if (ra0 == ra1 && ra1 == rb0 && rb0 == rb1)
return
(ra0 != null && (mvThresh(mvA0, mvB0) || mvThresh(mvA1, mvB0) || mvThresh(mvA0, mvB1) || mvThresh(mvA1, mvB1))) ? 1 :
0;
if (ra0 == rb0 && ra1 == rb1)
return ((ra0 != null && mvThresh(mvA0, mvB0)) || (ra1 != null && mvThresh(mvA1, mvB1))) ? 1 : 0;
if (ra0 == rb1 && ra1 == rb0)
return ((ra0 != null && mvThresh(mvA0, mvB1)) || (ra1 != null && mvThresh(mvA1, mvB0))) ? 1 : 0;
return 0;
}
private boolean mvThresh(int v0, int v1) {
return (Math.abs(H264Utils.Mv.mvX(v0) - H264Utils.Mv.mvX(v1)) >= 4 || Math.abs(H264Utils.Mv.mvY(v0) - H264Utils.Mv.mvY(v1)) >= 4);
}
private static int getIdxBeta(int sliceBetaOffset, int avgQp) {
return MathUtil.clip(avgQp + sliceBetaOffset, 0, 51);
}
private static int getIdxAlpha(int sliceAlphaC0Offset, int avgQp) {
return MathUtil.clip(avgQp + sliceAlphaC0Offset, 0, 51);
}
private void calcBsH(Picture pic, int mbAddr, int[][] bs) {
SliceHeader sh = this.di.shs[mbAddr];
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
int mbX = mbAddr % mbWidth;
int mbY = mbAddr / mbWidth;
boolean topAvailable = (mbY > 0 && (sh.disableDeblockingFilterIdc != 2 || this.di.shs[mbAddr - mbWidth] == sh));
boolean thisIntra = (this.di.mbTypes[mbAddr] != null && this.di.mbTypes[mbAddr].isIntra());
if (topAvailable) {
boolean topIntra = (this.di.mbTypes[mbAddr - mbWidth] != null && this.di.mbTypes[mbAddr - mbWidth].isIntra());
for (int blkX = 0; blkX < 4; blkX++) {
int thisBlkX = (mbX << 2) + blkX;
int thisBlkY = mbY << 2;
bs[0][blkX] = calcBoundaryStrenth(true, topIntra, thisIntra, this.di.nCoeff[thisBlkY][thisBlkX], this.di.nCoeff[thisBlkY - 1][thisBlkX],
this.di.mvs.getMv(thisBlkX, thisBlkY, 0),
this.di.mvs.getMv(thisBlkX, thisBlkY - 1, 0), this.di.mvs.getMv(thisBlkX, thisBlkY, 1),
this.di.mvs.getMv(thisBlkX, thisBlkY - 1, 1), mbAddr, mbAddr - mbWidth);
}
}
for (int blkY = 1; blkY < 4; blkY++) {
for (int blkX = 0; blkX < 4; blkX++) {
int thisBlkX = (mbX << 2) + blkX;
int thisBlkY = (mbY << 2) + blkY;
bs[blkY][blkX] = calcBoundaryStrenth(false, thisIntra, thisIntra, this.di.nCoeff[thisBlkY][thisBlkX], this.di.nCoeff[thisBlkY - 1][thisBlkX],
this.di.mvs.getMv(thisBlkX, thisBlkY, 0),
this.di.mvs.getMv(thisBlkX, thisBlkY - 1, 0), this.di.mvs.getMv(thisBlkX, thisBlkY, 1),
this.di.mvs.getMv(thisBlkX, thisBlkY - 1, 1), mbAddr, mbAddr);
}
}
}
private void fillHorizontalEdge(Picture pic, int comp, int mbAddr, int[][] bs) {
SliceHeader sh = this.di.shs[mbAddr];
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
int alpha = sh.sliceAlphaC0OffsetDiv2 << 1;
int beta = sh.sliceBetaOffsetDiv2 << 1;
int mbX = mbAddr % mbWidth;
int mbY = mbAddr / mbWidth;
boolean topAvailable = (mbY > 0 && (sh.disableDeblockingFilterIdc != 2 || this.di.shs[mbAddr - mbWidth] == sh));
int curQp = this.di.mbQps[comp][mbAddr];
int cW = 2 - (pic.getColor()).compWidth[comp];
int cH = 2 - (pic.getColor()).compHeight[comp];
if (topAvailable) {
int topQp = this.di.mbQps[comp][mbAddr - mbWidth];
int avgQp = topQp + curQp + 1 >> 1;
for (int blkX = 0; blkX < 4; blkX++) {
int thisBlkX = (mbX << 2) + blkX;
int thisBlkY = mbY << 2;
filterBlockEdgeHoris(pic, comp, thisBlkX << cW, thisBlkY << cH, getIdxAlpha(alpha, avgQp),
getIdxBeta(beta, avgQp), bs[0][blkX], 1 << cW);
}
}
boolean skip4x4 = ((comp == 0 && this.di.tr8x8Used[mbAddr]) || cH == 1);
for (int blkY = 1; blkY < 4; blkY++) {
if (!skip4x4 || (blkY & 0x1) != 1)
for (int blkX = 0; blkX < 4; blkX++) {
int thisBlkX = (mbX << 2) + blkX;
int thisBlkY = (mbY << 2) + blkY;
filterBlockEdgeHoris(pic, comp, thisBlkX << cW, thisBlkY << cH, getIdxAlpha(alpha, curQp),
getIdxBeta(beta, curQp), bs[blkY][blkX], 1 << cW);
}
}
}
private void calcBsV(Picture pic, int mbAddr, int[][] bs) {
SliceHeader sh = this.di.shs[mbAddr];
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
int mbX = mbAddr % mbWidth;
int mbY = mbAddr / mbWidth;
boolean leftAvailable = (mbX > 0 && (sh.disableDeblockingFilterIdc != 2 || this.di.shs[mbAddr - 1] == sh));
boolean thisIntra = (this.di.mbTypes[mbAddr] != null && this.di.mbTypes[mbAddr].isIntra());
if (leftAvailable) {
boolean leftIntra = (this.di.mbTypes[mbAddr - 1] != null && this.di.mbTypes[mbAddr - 1].isIntra());
for (int blkY = 0; blkY < 4; blkY++) {
int thisBlkX = mbX << 2;
int thisBlkY = (mbY << 2) + blkY;
bs[blkY][0] = calcBoundaryStrenth(true, leftIntra, thisIntra, this.di.nCoeff[thisBlkY][thisBlkX], this.di.nCoeff[thisBlkY][thisBlkX - 1],
this.di.mvs.getMv(thisBlkX, thisBlkY, 0),
this.di.mvs.getMv(thisBlkX - 1, thisBlkY, 0), this.di.mvs.getMv(thisBlkX, thisBlkY, 1),
this.di.mvs.getMv(thisBlkX - 1, thisBlkY, 1), mbAddr, mbAddr - 1);
}
}
for (int blkX = 1; blkX < 4; blkX++) {
for (int blkY = 0; blkY < 4; blkY++) {
int thisBlkX = (mbX << 2) + blkX;
int thisBlkY = (mbY << 2) + blkY;
bs[blkY][blkX] = calcBoundaryStrenth(false, thisIntra, thisIntra, this.di.nCoeff[thisBlkY][thisBlkX], this.di.nCoeff[thisBlkY][thisBlkX - 1],
this.di.mvs.getMv(thisBlkX, thisBlkY, 0),
this.di.mvs.getMv(thisBlkX - 1, thisBlkY, 0), this.di.mvs.getMv(thisBlkX, thisBlkY, 1),
this.di.mvs.getMv(thisBlkX - 1, thisBlkY, 1), mbAddr, mbAddr);
}
}
}
private void fillVerticalEdge(Picture pic, int comp, int mbAddr, int[][] bs) {
SliceHeader sh = this.di.shs[mbAddr];
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
int alpha = sh.sliceAlphaC0OffsetDiv2 << 1;
int beta = sh.sliceBetaOffsetDiv2 << 1;
int mbX = mbAddr % mbWidth;
int mbY = mbAddr / mbWidth;
boolean leftAvailable = (mbX > 0 && (sh.disableDeblockingFilterIdc != 2 || this.di.shs[mbAddr - 1] == sh));
int curQp = this.di.mbQps[comp][mbAddr];
int cW = 2 - (pic.getColor()).compWidth[comp];
int cH = 2 - (pic.getColor()).compHeight[comp];
if (leftAvailable) {
int leftQp = this.di.mbQps[comp][mbAddr - 1];
int avgQpV = leftQp + curQp + 1 >> 1;
for (int blkY = 0; blkY < 4; blkY++) {
int thisBlkX = mbX << 2;
int thisBlkY = (mbY << 2) + blkY;
filterBlockEdgeVert(pic, comp, thisBlkX << cW, thisBlkY << cH, getIdxAlpha(alpha, avgQpV),
getIdxBeta(beta, avgQpV), bs[blkY][0], 1 << cH);
}
}
boolean skip4x4 = ((comp == 0 && this.di.tr8x8Used[mbAddr]) || cW == 1);
for (int blkX = 1; blkX < 4; blkX++) {
if (!skip4x4 || (blkX & 0x1) != 1)
for (int blkY = 0; blkY < 4; blkY++) {
int thisBlkX = (mbX << 2) + blkX;
int thisBlkY = (mbY << 2) + blkY;
filterBlockEdgeVert(pic, comp, thisBlkX << cW, thisBlkY << cH, getIdxAlpha(alpha, curQp),
getIdxBeta(beta, curQp), bs[blkY][blkX], 1 << cH);
}
}
}
private void filterBlockEdgeHoris(Picture pic, int comp, int x, int y, int indexAlpha, int indexBeta, int bs, int blkW) {
int stride = pic.getPlaneWidth(comp);
int offset = y * stride + x;
for (int pixOff = 0; pixOff < blkW; pixOff++) {
int p2Idx = offset - 3 * stride + pixOff;
int p1Idx = offset - 2 * stride + pixOff;
int p0Idx = offset - stride + pixOff;
int q0Idx = offset + pixOff;
int q1Idx = offset + stride + pixOff;
int q2Idx = offset + 2 * stride + pixOff;
if (bs == 4) {
int p3Idx = offset - 4 * stride + pixOff;
int q3Idx = offset + 3 * stride + pixOff;
filterBs4(indexAlpha, indexBeta, pic.getPlaneData(comp), pic.getPlaneData(comp), p3Idx, p2Idx, p1Idx, p0Idx, q0Idx, q1Idx, q2Idx, q3Idx, (comp != 0));
} else if (bs > 0) {
filterBs(bs, indexAlpha, indexBeta, pic.getPlaneData(comp), pic.getPlaneData(comp), p2Idx, p1Idx, p0Idx, q0Idx, q1Idx, q2Idx, (comp != 0));
}
}
}
private void filterBlockEdgeVert(Picture pic, int comp, int x, int y, int indexAlpha, int indexBeta, int bs, int blkH) {
int stride = pic.getPlaneWidth(comp);
for (int i = 0; i < blkH; i++) {
int offsetQ = (y + i) * stride + x;
int p2Idx = offsetQ - 3;
int p1Idx = offsetQ - 2;
int p0Idx = offsetQ - 1;
int q0Idx = offsetQ;
int q1Idx = offsetQ + 1;
int q2Idx = offsetQ + 2;
if (bs == 4) {
int p3Idx = offsetQ - 4;
int q3Idx = offsetQ + 3;
filterBs4(indexAlpha, indexBeta, pic.getPlaneData(comp), pic.getPlaneData(comp), p3Idx, p2Idx, p1Idx, p0Idx, q0Idx, q1Idx, q2Idx, q3Idx, (comp != 0));
} else if (bs > 0) {
filterBs(bs, indexAlpha, indexBeta, pic.getPlaneData(comp), pic.getPlaneData(comp), p2Idx, p1Idx, p0Idx, q0Idx, q1Idx, q2Idx, (comp != 0));
}
}
}
public static void filterBs(int bs, int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p2Idx, int p1Idx, int p0Idx, int q0Idx, int q1Idx, int q2Idx, boolean isChroma) {
boolean conditionP, conditionQ;
int tC;
int p1 = pelsP[p1Idx];
int p0 = pelsP[p0Idx];
int q0 = pelsQ[q0Idx];
int q1 = pelsQ[q1Idx];
int alphaThresh = alphaTab[indexAlpha];
int betaThresh = betaTab[indexBeta];
boolean filterEnabled = (Math.abs(p0 - q0) < alphaThresh && Math.abs(p1 - p0) < betaThresh && Math.abs(q1 - q0) < betaThresh);
if (!filterEnabled)
return;
int tC0 = tcs[bs - 1][indexAlpha];
if (!isChroma) {
int ap = Math.abs(pelsP[p2Idx] - p0);
int aq = Math.abs(pelsQ[q2Idx] - q0);
tC = tC0 + ((ap < betaThresh) ? 1 : 0) + ((aq < betaThresh) ? 1 : 0);
conditionP = (ap < betaThresh);
conditionQ = (aq < betaThresh);
} else {
tC = tC0 + 1;
conditionP = false;
conditionQ = false;
}
int sigma = (q0 - p0 << 2) + p1 - q1 + 4 >> 3;
sigma = (sigma < -tC) ? -tC : ((sigma > tC) ? tC : sigma);
int p0n = p0 + sigma;
p0n = (p0n < -128) ? -128 : p0n;
int q0n = q0 - sigma;
q0n = (q0n < -128) ? -128 : q0n;
if (conditionP) {
int p2 = pelsP[p2Idx];
int diff = p2 + (p0 + q0 + 1 >> 1) - (p1 << 1) >> 1;
diff = (diff < -tC0) ? -tC0 : ((diff > tC0) ? tC0 : diff);
int p1n = p1 + diff;
pelsP[p1Idx] = (byte)MathUtil.clip(p1n, -128, 127);
}
if (conditionQ) {
int q2 = pelsQ[q2Idx];
int diff = q2 + (p0 + q0 + 1 >> 1) - (q1 << 1) >> 1;
diff = (diff < -tC0) ? -tC0 : ((diff > tC0) ? tC0 : diff);
int q1n = q1 + diff;
pelsQ[q1Idx] = (byte)MathUtil.clip(q1n, -128, 127);
}
pelsQ[q0Idx] = (byte)MathUtil.clip(q0n, -128, 127);
pelsP[p0Idx] = (byte)MathUtil.clip(p0n, -128, 127);
}
public static void filterBs4(int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p3Idx, int p2Idx, int p1Idx, int p0Idx, int q0Idx, int q1Idx, int q2Idx, int q3Idx, boolean isChroma) {
boolean conditionP, conditionQ;
int p0 = pelsP[p0Idx];
int q0 = pelsQ[q0Idx];
int p1 = pelsP[p1Idx];
int q1 = pelsQ[q1Idx];
int alphaThresh = alphaTab[indexAlpha];
int betaThresh = betaTab[indexBeta];
boolean filterEnabled = (Math.abs(p0 - q0) < alphaThresh && Math.abs(p1 - p0) < betaThresh && Math.abs(q1 - q0) < betaThresh);
if (!filterEnabled)
return;
if (isChroma) {
conditionP = false;
conditionQ = false;
} else {
int ap = Math.abs(pelsP[p2Idx] - p0);
int aq = Math.abs(pelsQ[q2Idx] - q0);
conditionP = (ap < betaThresh && Math.abs(p0 - q0) < (alphaThresh >> 2) + 2);
conditionQ = (aq < betaThresh && Math.abs(p0 - q0) < (alphaThresh >> 2) + 2);
}
if (conditionP) {
int p3 = pelsP[p3Idx];
int p2 = pelsP[p2Idx];
int p0n = p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4 >> 3;
int p1n = p2 + p1 + p0 + q0 + 2 >> 2;
int p2n = 2 * p3 + 3 * p2 + p1 + p0 + q0 + 4 >> 3;
pelsP[p0Idx] = (byte)MathUtil.clip(p0n, -128, 127);
pelsP[p1Idx] = (byte)MathUtil.clip(p1n, -128, 127);
pelsP[p2Idx] = (byte)MathUtil.clip(p2n, -128, 127);
} else {
int p0n = 2 * p1 + p0 + q1 + 2 >> 2;
pelsP[p0Idx] = (byte)MathUtil.clip(p0n, -128, 127);
}
if (conditionQ && !isChroma) {
int q2 = pelsQ[q2Idx];
int q3 = pelsQ[q3Idx];
int q0n = p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4 >> 3;
int q1n = p0 + q0 + q1 + q2 + 2 >> 2;
int q2n = 2 * q3 + 3 * q2 + q1 + q0 + p0 + 4 >> 3;
pelsQ[q0Idx] = (byte)MathUtil.clip(q0n, -128, 127);
pelsQ[q1Idx] = (byte)MathUtil.clip(q1n, -128, 127);
pelsQ[q2Idx] = (byte)MathUtil.clip(q2n, -128, 127);
} else {
int q0n = 2 * q1 + q0 + p1 + 2 >> 2;
pelsQ[q0Idx] = (byte)MathUtil.clip(q0n, -128, 127);
}
}
}

View file

@ -0,0 +1,41 @@
package org.jcodec.codecs.h264.encode;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.model.Size;
public class DumbRateControl implements RateControl {
private static final int QP = 20;
private int bitsPerMb;
private int totalQpDelta;
private boolean justSwitched;
public int accept(int bits) {
if (bits >= this.bitsPerMb) {
this.totalQpDelta++;
this.justSwitched = true;
return 1;
}
if (this.totalQpDelta > 0 && !this.justSwitched && this.bitsPerMb - bits > this.bitsPerMb >> 3) {
this.totalQpDelta--;
this.justSwitched = true;
return -1;
}
this.justSwitched = false;
return 0;
}
public int startPicture(Size sz, int maxSize, SliceType sliceType) {
int totalMb = (sz.getWidth() + 15 >> 4) * (sz.getHeight() + 15 >> 4);
this.bitsPerMb = (maxSize << 3) / totalMb;
this.totalQpDelta = 0;
this.justSwitched = false;
return 20 + ((sliceType == SliceType.P) ? 6 : 0);
}
public int initialQpDelta() {
return 0;
}
}

View file

@ -0,0 +1,51 @@
package org.jcodec.codecs.h264.encode;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
public class EncodedMB {
private Picture pixels = Picture.create(16, 16, ColorSpace.YUV420J);
private MBType type;
private int qp;
private int[] nc = new int[16];
private int[] mx = new int[16];
private int[] my = new int[16];
public Picture getPixels() {
return this.pixels;
}
public MBType getType() {
return this.type;
}
public void setType(MBType type) {
this.type = type;
}
public int getQp() {
return this.qp;
}
public void setQp(int qp) {
this.qp = qp;
}
public int[] getNc() {
return this.nc;
}
public int[] getMx() {
return this.mx;
}
public int[] getMy() {
return this.my;
}
}

View file

@ -0,0 +1,50 @@
package org.jcodec.codecs.h264.encode;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.model.Size;
import org.jcodec.common.tools.MathUtil;
public class H264FixedRateControl implements RateControl {
private static final int INIT_QP = 26;
private int balance;
private int perMb;
private int curQp;
public H264FixedRateControl(int bitsPer256) {
this.perMb = bitsPer256;
this.curQp = 26;
}
public int startPicture(Size sz, int maxSize, SliceType sliceType) {
return 26 + ((sliceType == SliceType.P) ? 4 : 0);
}
public int initialQpDelta() {
int qpDelta = (this.balance < 0) ? ((this.balance < -(this.perMb >> 1)) ? 2 : 1) : (
(this.balance > this.perMb) ? ((this.balance > this.perMb << 2) ? -2 : -1) : 0);
int prevQp = this.curQp;
this.curQp = MathUtil.clip(this.curQp + qpDelta, 12, 30);
return this.curQp - prevQp;
}
public int accept(int bits) {
this.balance += this.perMb - bits;
return 0;
}
public void reset() {
this.balance = 0;
this.curQp = 26;
}
public int calcFrameSize(int nMB) {
return (256 + nMB * (this.perMb + 9) >> 3) + (nMB >> 6);
}
public void setRate(int rate) {
this.perMb = rate;
}
}

View file

@ -0,0 +1,307 @@
package org.jcodec.codecs.h264.encode;
import org.jcodec.codecs.h264.decode.deblock.DeblockingFilter;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class MBDeblocker {
static int[][] LOOKUP_IDX_P_V = new int[][] { new int[] { 3, 7, 11, 15 }, new int[] { 0, 4, 8, 12 }, new int[] { 1, 5, 9, 13 }, new int[] { 2, 6, 10, 14 } };
static int[][] LOOKUP_IDX_Q_V = new int[][] { new int[] { 0, 4, 8, 12 }, new int[] { 1, 5, 9, 13 }, new int[] { 2, 6, 10, 14 }, new int[] { 3, 7, 11, 15 } };
static int[][] LOOKUP_IDX_P_H = new int[][] { new int[] { 12, 13, 14, 15 }, new int[] { 0, 1, 2, 3 }, new int[] { 4, 5, 6, 7 }, new int[] { 8, 9, 10, 11 } };
static int[][] LOOKUP_IDX_Q_H = new int[][] { new int[] { 0, 1, 2, 3 }, new int[] { 4, 5, 6, 7 }, new int[] { 8, 9, 10, 11 }, new int[] { 12, 13, 14, 15 } };
private static int[][] BS_I = new int[][] { new int[] { 4, 4, 4, 4 }, new int[] { 3, 3, 3, 3 }, new int[] { 3, 3, 3, 3 }, new int[] { 3, 3, 3, 3 } };
public void deblockMBGeneric(EncodedMB curMB, EncodedMB leftMB, EncodedMB topMB, int[][] vertStrength, int[][] horizStrength) {
Picture curPix = curMB.getPixels();
if (leftMB != null) {
Picture leftPix = leftMB.getPixels();
int avgQp = MathUtil.clip(leftMB.getQp() + curMB.getQp() + 1 >> 1, 0, 51);
deblockBorder(vertStrength[0], avgQp, leftPix.getPlaneData(0), 3, curPix.getPlaneData(0), 0, P_POS_V, Q_POS_V, false);
deblockBorderChroma(vertStrength[0], avgQp, leftPix.getPlaneData(1), 3, curPix.getPlaneData(1), 0, P_POS_V_CHR, Q_POS_V_CHR, false);
deblockBorderChroma(vertStrength[0], avgQp, leftPix.getPlaneData(2), 3, curPix.getPlaneData(2), 0, P_POS_V_CHR, Q_POS_V_CHR, false);
}
for (int j = 0; j < 3; j++) {
deblockBorder(vertStrength[j + 1], curMB.getQp(), curPix.getPlaneData(0), j, curPix.getPlaneData(0), j + 1, P_POS_V, Q_POS_V, false);
deblockBorderChroma(vertStrength[j + 1], curMB.getQp(), curPix.getPlaneData(1), j, curPix.getPlaneData(1), j + 1, P_POS_V_CHR, Q_POS_V_CHR, false);
deblockBorderChroma(vertStrength[j + 1], curMB.getQp(), curPix.getPlaneData(2), j, curPix.getPlaneData(2), j + 1, P_POS_V_CHR, Q_POS_V_CHR, false);
}
if (topMB != null) {
Picture topPix = topMB.getPixels();
int avgQp = MathUtil.clip(topMB.getQp() + curMB.getQp() + 1 >> 1, 0, 51);
deblockBorder(horizStrength[0], avgQp, topPix.getPlaneData(0), 3, curPix.getPlaneData(0), 0, P_POS_H, Q_POS_H, true);
deblockBorderChroma(horizStrength[0], avgQp, topPix.getPlaneData(1), 3, curPix.getPlaneData(1), 0, P_POS_H_CHR, Q_POS_H_CHR, true);
deblockBorderChroma(horizStrength[0], avgQp, topPix.getPlaneData(2), 3, curPix.getPlaneData(2), 0, P_POS_H_CHR, Q_POS_H_CHR, true);
}
for (int i = 0; i < 3; i++) {
deblockBorder(horizStrength[i + 1], curMB.getQp(), curPix.getPlaneData(0), i, curPix.getPlaneData(0), i + 1, P_POS_H, Q_POS_H, true);
deblockBorderChroma(horizStrength[i + 1], curMB.getQp(), curPix.getPlaneData(1), i, curPix.getPlaneData(1), i + 1, P_POS_H_CHR, Q_POS_H_CHR, true);
deblockBorderChroma(horizStrength[i + 1], curMB.getQp(), curPix.getPlaneData(2), i, curPix.getPlaneData(2), i + 1, P_POS_H_CHR, Q_POS_H_CHR, true);
}
}
public void deblockMBI(EncodedMB outMB, EncodedMB leftOutMB, EncodedMB topOutMB) {
deblockMBGeneric(outMB, leftOutMB, topOutMB, BS_I, BS_I);
}
public void deblockMBP(EncodedMB cur, EncodedMB left, EncodedMB top) {
int[][] vertStrength = new int[4][4];
int[][] horizStrength = new int[4][4];
calcStrengthForBlocks(cur, left, vertStrength, LOOKUP_IDX_P_V, LOOKUP_IDX_Q_V);
calcStrengthForBlocks(cur, top, horizStrength, LOOKUP_IDX_P_H, LOOKUP_IDX_Q_H);
deblockMBGeneric(cur, left, top, vertStrength, horizStrength);
}
private void deblockBorder(int[] boundary, int qp, byte[] p, int pi, byte[] q, int qi, int[][] pTab, int[][] qTab, boolean horiz) {
int inc1 = horiz ? 16 : 1, inc2 = inc1 * 2, inc3 = inc1 * 3;
for (int b = 0; b < 4; b++) {
if (boundary[b] == 4) {
for (int i = 0, ii = b << 2; i < 4; i++, ii++)
filterBs4(qp, qp, p, q, pTab[pi][ii] - inc3, pTab[pi][ii] - inc2, pTab[pi][ii] - inc1, pTab[pi][ii], qTab[qi][ii], qTab[qi][ii] + inc1, qTab[qi][ii] + inc2, qTab[qi][ii] + inc3);
} else if (boundary[b] > 0) {
for (int i = 0, ii = b << 2; i < 4; i++, ii++)
filterBs(boundary[b], qp, qp, p, q, pTab[pi][ii] - inc2, pTab[pi][ii] - inc1, pTab[pi][ii], qTab[qi][ii], qTab[qi][ii] + inc1, qTab[qi][ii] + inc2);
}
}
}
protected void filterBs4Chr(int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p1Idx, int p0Idx, int q0Idx, int q1Idx) {
_filterBs4(indexAlpha, indexBeta, pelsP, pelsQ, -1, -1, p1Idx, p0Idx, q0Idx, q1Idx, -1, -1, true);
}
protected void filterBsChr(int bs, int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p1Idx, int p0Idx, int q0Idx, int q1Idx) {
_filterBs(bs, indexAlpha, indexBeta, pelsP, pelsQ, -1, p1Idx, p0Idx, q0Idx, q1Idx, -1, true);
}
protected void filterBs4(int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p3Idx, int p2Idx, int p1Idx, int p0Idx, int q0Idx, int q1Idx, int q2Idx, int q3Idx) {
_filterBs4(indexAlpha, indexBeta, pelsP, pelsQ, p3Idx, p2Idx, p1Idx, p0Idx, q0Idx, q1Idx, q2Idx, q3Idx, false);
}
protected void filterBs(int bs, int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p2Idx, int p1Idx, int p0Idx, int q0Idx, int q1Idx, int q2Idx) {
_filterBs(bs, indexAlpha, indexBeta, pelsP, pelsQ, p2Idx, p1Idx, p0Idx, q0Idx, q1Idx, q2Idx, false);
}
protected void _filterBs4(int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p3Idx, int p2Idx, int p1Idx, int p0Idx, int q0Idx, int q1Idx, int q2Idx, int q3Idx, boolean isChroma) {
boolean conditionP, conditionQ;
int p0 = pelsP[p0Idx];
int q0 = pelsQ[q0Idx];
int p1 = pelsP[p1Idx];
int q1 = pelsQ[q1Idx];
int alphaThresh = DeblockingFilter.alphaTab[indexAlpha];
int betaThresh = DeblockingFilter.betaTab[indexBeta];
boolean filterEnabled = (Math.abs(p0 - q0) < alphaThresh && Math.abs(p1 - p0) < betaThresh && Math.abs(q1 - q0) < betaThresh);
if (!filterEnabled)
return;
if (isChroma) {
conditionP = false;
conditionQ = false;
} else {
int ap = Math.abs(pelsP[p2Idx] - p0);
int aq = Math.abs(pelsQ[q2Idx] - q0);
conditionP = (ap < betaThresh && Math.abs(p0 - q0) < (alphaThresh >> 2) + 2);
conditionQ = (aq < betaThresh && Math.abs(p0 - q0) < (alphaThresh >> 2) + 2);
}
if (conditionP) {
int p3 = pelsP[p3Idx];
int p2 = pelsP[p2Idx];
int p0n = p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4 >> 3;
int p1n = p2 + p1 + p0 + q0 + 2 >> 2;
int p2n = 2 * p3 + 3 * p2 + p1 + p0 + q0 + 4 >> 3;
pelsP[p0Idx] = (byte)MathUtil.clip(p0n, -128, 127);
pelsP[p1Idx] = (byte)MathUtil.clip(p1n, -128, 127);
pelsP[p2Idx] = (byte)MathUtil.clip(p2n, -128, 127);
} else {
int p0n = 2 * p1 + p0 + q1 + 2 >> 2;
pelsP[p0Idx] = (byte)MathUtil.clip(p0n, -128, 127);
}
if (conditionQ && !isChroma) {
int q2 = pelsQ[q2Idx];
int q3 = pelsQ[q3Idx];
int q0n = p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4 >> 3;
int q1n = p0 + q0 + q1 + q2 + 2 >> 2;
int q2n = 2 * q3 + 3 * q2 + q1 + q0 + p0 + 4 >> 3;
pelsQ[q0Idx] = (byte)MathUtil.clip(q0n, -128, 127);
pelsQ[q1Idx] = (byte)MathUtil.clip(q1n, -128, 127);
pelsQ[q2Idx] = (byte)MathUtil.clip(q2n, -128, 127);
} else {
int q0n = 2 * q1 + q0 + p1 + 2 >> 2;
pelsQ[q0Idx] = (byte)MathUtil.clip(q0n, -128, 127);
}
}
protected void _filterBs(int bs, int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p2Idx, int p1Idx, int p0Idx, int q0Idx, int q1Idx, int q2Idx, boolean isChroma) {
boolean conditionP, conditionQ;
int tC;
int p1 = pelsP[p1Idx];
int p0 = pelsP[p0Idx];
int q0 = pelsQ[q0Idx];
int q1 = pelsQ[q1Idx];
int alphaThresh = DeblockingFilter.alphaTab[indexAlpha];
int betaThresh = DeblockingFilter.betaTab[indexBeta];
boolean filterEnabled = (Math.abs(p0 - q0) < alphaThresh && Math.abs(p1 - p0) < betaThresh && Math.abs(q1 - q0) < betaThresh);
if (!filterEnabled)
return;
int tC0 = DeblockingFilter.tcs[bs - 1][indexAlpha];
if (!isChroma) {
int ap = Math.abs(pelsP[p2Idx] - p0);
int aq = Math.abs(pelsQ[q2Idx] - q0);
tC = tC0 + ((ap < betaThresh) ? 1 : 0) + ((aq < betaThresh) ? 1 : 0);
conditionP = (ap < betaThresh);
conditionQ = (aq < betaThresh);
} else {
tC = tC0 + 1;
conditionP = false;
conditionQ = false;
}
int sigma = (q0 - p0 << 2) + p1 - q1 + 4 >> 3;
sigma = (sigma < -tC) ? -tC : ((sigma > tC) ? tC : sigma);
int p0n = p0 + sigma;
p0n = (p0n < -128) ? -128 : p0n;
int q0n = q0 - sigma;
q0n = (q0n < -128) ? -128 : q0n;
if (conditionP) {
int p2 = pelsP[p2Idx];
int diff = p2 + (p0 + q0 + 1 >> 1) - (p1 << 1) >> 1;
diff = (diff < -tC0) ? -tC0 : ((diff > tC0) ? tC0 : diff);
int p1n = p1 + diff;
pelsP[p1Idx] = (byte)MathUtil.clip(p1n, -128, 127);
}
if (conditionQ) {
int q2 = pelsQ[q2Idx];
int diff = q2 + (p0 + q0 + 1 >> 1) - (q1 << 1) >> 1;
diff = (diff < -tC0) ? -tC0 : ((diff > tC0) ? tC0 : diff);
int q1n = q1 + diff;
pelsQ[q1Idx] = (byte)MathUtil.clip(q1n, -128, 127);
}
pelsQ[q0Idx] = (byte)MathUtil.clip(q0n, -128, 127);
pelsP[p0Idx] = (byte)MathUtil.clip(p0n, -128, 127);
}
private void deblockBorderChroma(int[] boundary, int qp, byte[] p, int pi, byte[] q, int qi, int[][] pTab, int[][] qTab, boolean horiz) {
int inc1 = horiz ? 8 : 1;
for (int b = 0; b < 4; b++) {
if (boundary[b] == 4) {
for (int i = 0, ii = b << 1; i < 2; i++, ii++)
filterBs4Chr(qp, qp, p, q, pTab[pi][ii] - inc1, pTab[pi][ii], qTab[qi][ii], qTab[qi][ii] + inc1);
} else if (boundary[b] > 0) {
for (int i = 0, ii = b << 1; i < 2; i++, ii++)
filterBsChr(boundary[b], qp, qp, p, q, pTab[pi][ii] - inc1, pTab[pi][ii], qTab[qi][ii], qTab[qi][ii] + inc1);
}
}
}
private static int[][] buildPPosH() {
int[][] qPos = new int[4][16];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 16; j++)
qPos[i][j] = j + (i << 6) + 48;
}
return qPos;
}
private static int[][] buildQPosH() {
int[][] pPos = new int[4][16];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 16; j++)
pPos[i][j] = j + (i << 6);
}
return pPos;
}
private static int[][] buildPPosV() {
int[][] qPos = new int[4][16];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 16; j++)
qPos[i][j] = (j << 4) + (i << 2) + 3;
}
return qPos;
}
private static int[][] buildQPosV() {
int[][] pPos = new int[4][16];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 16; j++)
pPos[i][j] = (j << 4) + (i << 2);
}
return pPos;
}
private static int[][] buildPPosHChr() {
int[][] qPos = new int[4][8];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 8; j++)
qPos[i][j] = j + (i << 4) + 8;
}
return qPos;
}
private static int[][] buildQPosHChr() {
int[][] pPos = new int[4][8];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 8; j++)
pPos[i][j] = j + (i << 4);
}
return pPos;
}
private static int[][] buildPPosVChr() {
int[][] qPos = new int[4][8];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 8; j++)
qPos[i][j] = (j << 3) + (i << 1) + 1;
}
return qPos;
}
private static int[][] buildQPosVChr() {
int[][] pPos = new int[4][8];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 8; j++)
pPos[i][j] = (j << 3) + (i << 1);
}
return pPos;
}
static void calcStrengthForBlocks(EncodedMB cur, EncodedMB other, int[][] outStrength, int[][] LOOKUP_IDX_P, int[][] LOOKUP_IDX_Q) {
if (other != null)
for (int j = 0; j < 4; j++)
outStrength[0][j] = other.getType().isIntra() ? 4 : MathUtil.max3(
strengthMv(other.getMx()[LOOKUP_IDX_P[0][j]], cur.getMx()[LOOKUP_IDX_Q[0][j]]),
strengthMv(other.getMy()[LOOKUP_IDX_P[0][j]], cur.getMy()[LOOKUP_IDX_Q[0][j]]),
strengthNc(other.getNc()[LOOKUP_IDX_P[0][j]], cur.getNc()[LOOKUP_IDX_Q[0][j]]));
for (int i = 1; i < 4; i++) {
for (int j = 0; j < 4; j++)
outStrength[i][j] = MathUtil.max3(
strengthMv(cur.getMx()[LOOKUP_IDX_P[i][j]], cur.getMx()[LOOKUP_IDX_Q[i][j]]),
strengthMv(cur.getMy()[LOOKUP_IDX_P[i][j]], cur.getMy()[LOOKUP_IDX_Q[i][j]]),
strengthNc(cur.getNc()[LOOKUP_IDX_P[i][j]], cur.getNc()[LOOKUP_IDX_Q[i][j]]));
}
}
private static int strengthNc(int ncA, int ncB) {
return (ncA > 0 || ncB > 0) ? 2 : 0;
}
private static int strengthMv(int v0, int v1) {
return (Math.abs(v0 - v1) >= 4) ? 1 : 0;
}
private static int[][] P_POS_V = buildPPosV();
private static int[][] Q_POS_V = buildQPosV();
private static int[][] P_POS_H = buildPPosH();
private static int[][] Q_POS_H = buildQPosH();
private static int[][] P_POS_V_CHR = buildPPosVChr();
private static int[][] Q_POS_V_CHR = buildQPosVChr();
private static int[][] P_POS_H_CHR = buildPPosHChr();
private static int[][] Q_POS_H_CHR = buildQPosHChr();
}

View file

@ -0,0 +1,127 @@
package org.jcodec.codecs.h264.encode;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class MBEncoderHelper {
public static final void takeSubtract(byte[] planeData, int planeWidth, int planeHeight, int x, int y, int[] coeff, byte[] pred, int blkW, int blkH) {
if (x + blkW < planeWidth && y + blkH < planeHeight) {
takeSubtractSafe(planeData, planeWidth, planeHeight, x, y, coeff, pred, blkW, blkH);
} else {
takeSubtractUnsafe(planeData, planeWidth, planeHeight, x, y, coeff, pred, blkW, blkH);
}
}
public static final void takeSubtractSafe(byte[] planeData, int planeWidth, int planeHeight, int x, int y, int[] coeff, byte[] pred, int blkW, int blkH) {
for (int i = 0, srcOff = y * planeWidth + x, dstOff = 0; i < blkH; i++, srcOff += planeWidth) {
for (int j = 0, srcOff1 = srcOff; j < blkW; j += 4, dstOff += 4, srcOff1 += 4) {
coeff[dstOff] = planeData[srcOff1] - pred[dstOff];
coeff[dstOff + 1] = planeData[srcOff1 + 1] - pred[dstOff + 1];
coeff[dstOff + 2] = planeData[srcOff1 + 2] - pred[dstOff + 2];
coeff[dstOff + 3] = planeData[srcOff1 + 3] - pred[dstOff + 3];
}
}
}
public static final void take(byte[] planeData, int planeWidth, int planeHeight, int x, int y, byte[] patch, int blkW, int blkH) {
if (x + blkW < planeWidth && y + blkH < planeHeight) {
takeSafe(planeData, planeWidth, planeHeight, x, y, patch, blkW, blkH);
} else {
takeExtendBorder(planeData, planeWidth, planeHeight, x, y, patch, blkW, blkH);
}
}
public static final void takeSafe(byte[] planeData, int planeWidth, int planeHeight, int x, int y, byte[] patch, int blkW, int blkH) {
for (int i = 0, srcOff = y * planeWidth + x, dstOff = 0; i < blkH; i++, srcOff += planeWidth) {
for (int j = 0, srcOff1 = srcOff; j < blkW; j++, dstOff++, srcOff1++)
patch[dstOff] = planeData[srcOff1];
}
}
public static final void takeExtendBorder(byte[] planeData, int planeWidth, int planeHeight, int x, int y, byte[] patch, int blkW, int blkH) {
int outOff = 0;
int i;
for (i = y; i < Math.min(y + blkH, planeHeight); i++) {
int off = i * planeWidth + Math.min(x, planeWidth);
int j;
for (j = x; j < Math.min(x + blkW, planeWidth); j++, outOff++, off++)
patch[outOff] = planeData[off];
off--;
for (; j < x + blkW; j++, outOff++)
patch[outOff] = planeData[off];
}
for (; i < y + blkH; i++) {
int off = planeHeight * planeWidth - planeWidth + Math.min(x, planeWidth);
int j;
for (j = x; j < Math.min(x + blkW, planeWidth); j++, outOff++, off++)
patch[outOff] = planeData[off];
off--;
for (; j < x + blkW; j++, outOff++)
patch[outOff] = planeData[off];
}
}
public static final void takeSafe2(byte[] planeData, int planeWidth, int planeHeight, int x, int y, int[] coeff, int blkW, int blkH) {
for (int i = 0, srcOff = y * planeWidth + x, dstOff = 0; i < blkH; i++, srcOff += planeWidth) {
for (int j = 0, srcOff1 = srcOff; j < blkW; j++, dstOff++, srcOff1++)
coeff[dstOff] = planeData[srcOff1];
}
}
public static final void takeSubtractUnsafe(byte[] planeData, int planeWidth, int planeHeight, int x, int y, int[] coeff, byte[] pred, int blkW, int blkH) {
int outOff = 0;
int i;
for (i = y; i < Math.min(y + blkH, planeHeight); i++) {
int off = i * planeWidth + Math.min(x, planeWidth);
int j;
for (j = x; j < Math.min(x + blkW, planeWidth); j++, outOff++, off++)
coeff[outOff] = planeData[off] - pred[outOff];
off--;
for (; j < x + blkW; j++, outOff++)
coeff[outOff] = planeData[off] - pred[outOff];
}
for (; i < y + blkH; i++) {
int off = planeHeight * planeWidth - planeWidth + Math.min(x, planeWidth);
int j;
for (j = x; j < Math.min(x + blkW, planeWidth); j++, outOff++, off++)
coeff[outOff] = planeData[off] - pred[outOff];
off--;
for (; j < x + blkW; j++, outOff++)
coeff[outOff] = planeData[off] - pred[outOff];
}
}
public static final void putBlk(byte[] planeData, int[] block, byte[] pred, int log2stride, int blkX, int blkY, int blkW, int blkH) {
int stride = 1 << log2stride;
for (int line = 0, srcOff = 0, dstOff = (blkY << log2stride) + blkX; line < blkH; line++) {
int dstOff1 = dstOff;
for (int row = 0; row < blkW; row += 4) {
planeData[dstOff1] = (byte)MathUtil.clip(block[srcOff] + pred[srcOff], -128, 127);
planeData[dstOff1 + 1] = (byte)MathUtil.clip(block[srcOff + 1] + pred[srcOff + 1], -128, 127);
planeData[dstOff1 + 2] = (byte)MathUtil.clip(block[srcOff + 2] + pred[srcOff + 2], -128, 127);
planeData[dstOff1 + 3] = (byte)MathUtil.clip(block[srcOff + 3] + pred[srcOff + 3], -128, 127);
srcOff += 4;
dstOff1 += 4;
}
dstOff += stride;
}
}
public static final void putBlkPic(Picture dest, Picture src, int x, int y) {
if (dest.getColor() != src.getColor())
throw new RuntimeException("Incompatible color");
for (int c = 0; c < (dest.getColor()).nComp; c++)
pubBlkOnePlane(dest.getPlaneData(c), dest.getPlaneWidth(c), src.getPlaneData(c), src.getPlaneWidth(c),
src.getPlaneHeight(c), x >> (dest.getColor()).compWidth[c], y >> (dest.getColor()).compHeight[c]);
}
private static void pubBlkOnePlane(byte[] dest, int destWidth, byte[] src, int srcWidth, int srcHeight, int x, int y) {
int destOff = y * destWidth + x;
int srcOff = 0;
for (int i = 0; i < srcHeight; i++) {
for (int j = 0; j < srcWidth; j++, destOff++, srcOff++)
dest[destOff] = src[srcOff];
destOff += destWidth - srcWidth;
}
}
}

View file

@ -0,0 +1,269 @@
package org.jcodec.codecs.h264.encode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.decode.CoeffTransformer;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.common.ArrayUtil;
import org.jcodec.common.SaveRestore;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.model.Picture;
public class MBEncoderI16x16 implements SaveRestore {
private CAVLC[] cavlc;
private byte[][] leftRow;
private byte[][] topLine;
public MBEncoderI16x16(CAVLC[] cavlc, byte[][] leftRow, byte[][] topLine) {
this.cavlc = cavlc;
this.leftRow = leftRow;
this.topLine = topLine;
}
public void save() {
for (int i = 0; i < this.cavlc.length; i++)
this.cavlc[i].save();
}
public void restore() {
for (int i = 0; i < this.cavlc.length; i++)
this.cavlc[i].restore();
}
public void encodeMacroblock(Picture pic, int mbX, int mbY, BitWriter out, EncodedMB outMB, EncodedMB leftOutMB, EncodedMB topOutMB, int qp, int qpDelta) {
CAVLCWriter.writeUE(out, 0);
CAVLCWriter.writeSE(out, qpDelta);
outMB.setType(MBType.I_16x16);
outMB.setQp(qp);
luma(pic, mbX, mbY, out, qp, outMB.getPixels(), this.cavlc[0]);
chroma(pic, mbX, mbY, out, qp, outMB.getPixels());
new MBDeblocker().deblockMBI(outMB, leftOutMB, topOutMB);
}
private static int[] DUMMY = new int[16];
private void chroma(Picture pic, int mbX, int mbY, BitWriter out, int qp, Picture outMB) {
int x = mbX << 3;
int y = mbY << 3;
int[][] ac1 = new int[4][16];
int[][] ac2 = new int[4][16];
byte[][] pred1 = new byte[4][16];
byte[][] pred2 = new byte[4][16];
predictChroma(pic, ac1, pred1, 1, x, y);
predictChroma(pic, ac2, pred2, 2, x, y);
chromaResidual(pic, mbX, mbY, out, qp, ac1, ac2, this.cavlc[1], this.cavlc[2], MBType.I_16x16, MBType.I_16x16);
putChroma(outMB.getData()[1], 1, x, y, ac1, pred1);
putChroma(outMB.getData()[2], 2, x, y, ac2, pred2);
}
public static void chromaResidual(Picture pic, int mbX, int mbY, BitWriter out, int qp, int[][] ac1, int[][] ac2, CAVLC cavlc1, CAVLC cavlc2, MBType leftMBType, MBType topMBType) {
transformChroma(ac1);
transformChroma(ac2);
int[] dc1 = extractDC(ac1);
int[] dc2 = extractDC(ac2);
writeDC(cavlc1, mbX, mbY, out, qp, mbX << 1, mbY << 1, dc1, leftMBType, topMBType);
writeDC(cavlc2, mbX, mbY, out, qp, mbX << 1, mbY << 1, dc2, leftMBType, topMBType);
writeAC(cavlc1, mbX, mbY, out, mbX << 1, mbY << 1, ac1, qp, leftMBType, topMBType, DUMMY);
writeAC(cavlc2, mbX, mbY, out, mbX << 1, mbY << 1, ac2, qp, leftMBType, topMBType, DUMMY);
restorePlane(dc1, ac1, qp);
restorePlane(dc2, ac2, qp);
}
private void luma(Picture pic, int mbX, int mbY, BitWriter out, int qp, Picture outMB, CAVLC cavlc) {
int x = mbX << 4;
int y = mbY << 4;
int[][] ac = new int[16][16];
byte[][] pred = new byte[16][16];
lumaDCPred(x, y, pred);
transform(pic, 0, ac, pred, x, y);
int[] dc = extractDC(ac);
writeDC(cavlc, mbX, mbY, out, qp, mbX << 2, mbY << 2, dc, MBType.I_16x16, MBType.I_16x16);
writeAC(cavlc, mbX, mbY, out, mbX << 2, mbY << 2, ac, qp, MBType.I_16x16, MBType.I_16x16, DUMMY);
restorePlane(dc, ac, qp);
for (int blk = 0; blk < ac.length; blk++)
MBEncoderHelper.putBlk(outMB.getPlaneData(0), ac[blk], pred[blk], 4, H264Const.BLK_X[blk], H264Const.BLK_Y[blk], 4, 4);
}
private void putChroma(byte[] mb, int comp, int x, int y, int[][] ac, byte[][] pred) {
MBEncoderHelper.putBlk(mb, ac[0], pred[0], 3, 0, 0, 4, 4);
MBEncoderHelper.putBlk(mb, ac[1], pred[1], 3, 4, 0, 4, 4);
MBEncoderHelper.putBlk(mb, ac[2], pred[2], 3, 0, 4, 4, 4);
MBEncoderHelper.putBlk(mb, ac[3], pred[3], 3, 4, 4, 4, 4);
}
private static void restorePlane(int[] dc, int[][] ac, int qp) {
if (dc.length == 4) {
CoeffTransformer.invDC2x2(dc);
CoeffTransformer.dequantizeDC2x2(dc, qp, null);
} else if (dc.length == 8) {
CoeffTransformer.invDC4x2(dc);
CoeffTransformer.dequantizeDC4x2(dc, qp);
} else {
CoeffTransformer.invDC4x4(dc);
CoeffTransformer.dequantizeDC4x4(dc, qp, null);
CoeffTransformer.reorderDC4x4(dc);
}
for (int i = 0; i < ac.length; i++) {
CoeffTransformer.dequantizeAC(ac[i], qp, null);
ac[i][0] = dc[i];
CoeffTransformer.idct4x4(ac[i]);
}
}
private static int[] extractDC(int[][] ac) {
int[] dc = new int[ac.length];
for (int i = 0; i < ac.length; i++) {
dc[i] = ac[i][0];
ac[i][0] = 0;
}
return dc;
}
private static void writeAC(CAVLC cavlc, int mbX, int mbY, BitWriter out, int mbLeftBlk, int mbTopBlk, int[][] ac, int qp, MBType leftMBType, MBType topMBType, int[] nc) {
for (int i = 0; i < ac.length; i++) {
CoeffTransformer.quantizeAC(ac[i], qp);
nc[H264Const.BLK_INV_MAP[i]] = CAVLC.totalCoeff(cavlc.writeACBlock(out, mbLeftBlk + H264Const.MB_BLK_OFF_LEFT[i], mbTopBlk + H264Const.MB_BLK_OFF_TOP[i], leftMBType, topMBType, ac[i], H264Const.totalZeros16, 1, 15, CoeffTransformer.zigzag4x4));
}
}
private static void writeDC(CAVLC cavlc, int mbX, int mbY, BitWriter out, int qp, int mbLeftBlk, int mbTopBlk, int[] dc, MBType leftMBType, MBType topMBType) {
if (dc.length == 4) {
CoeffTransformer.quantizeDC2x2(dc, qp);
CoeffTransformer.fvdDC2x2(dc);
cavlc.writeChrDCBlock(out, dc, H264Const.totalZeros4, 0, dc.length, new int[] { 0, 1, 2, 3 });
} else if (dc.length == 8) {
CoeffTransformer.quantizeDC4x2(dc, qp);
CoeffTransformer.fvdDC4x2(dc);
cavlc.writeChrDCBlock(out, dc, H264Const.totalZeros8, 0, dc.length, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
} else {
CoeffTransformer.reorderDC4x4(dc);
CoeffTransformer.quantizeDC4x4(dc, qp);
CoeffTransformer.fvdDC4x4(dc);
cavlc.writeLumaDCBlock(out, mbLeftBlk, mbTopBlk, leftMBType, topMBType, dc, H264Const.totalZeros16, 0, 16, CoeffTransformer.zigzag4x4);
}
}
private static void transformChroma(int[][] ac) {
for (int i = 0; i < 4; i++)
CoeffTransformer.fdct4x4(ac[i]);
}
private void predictChroma(Picture pic, int[][] ac, byte[][] pred, int comp, int x, int y) {
chromaPredBlk0(comp, x, y, pred[0]);
chromaPredBlk1(comp, x, y, pred[1]);
chromaPredBlk2(comp, x, y, pred[2]);
chromaPredBlk3(comp, x, y, pred[3]);
MBEncoderHelper.takeSubtract(pic.getPlaneData(comp), pic.getPlaneWidth(comp), pic.getPlaneHeight(comp), x, y, ac[0], pred[0], 4, 4);
MBEncoderHelper.takeSubtract(pic.getPlaneData(comp), pic.getPlaneWidth(comp), pic.getPlaneHeight(comp), x + 4, y, ac[1], pred[1], 4, 4);
MBEncoderHelper.takeSubtract(pic.getPlaneData(comp), pic.getPlaneWidth(comp), pic.getPlaneHeight(comp), x, y + 4, ac[2], pred[2], 4, 4);
MBEncoderHelper.takeSubtract(pic.getPlaneData(comp), pic.getPlaneWidth(comp), pic.getPlaneHeight(comp), x + 4, y + 4, ac[3], pred[3], 4, 4);
}
private final int chromaPredOne(byte[] pix, int x) {
return pix[x] + pix[x + 1] + pix[x + 2] + pix[x + 3] + 2 >> 2;
}
private final int chromaPredTwo(byte[] pix1, byte[] pix2, int x, int y) {
return pix1[x] + pix1[x + 1] + pix1[x + 2] + pix1[x + 3] + pix2[y] + pix2[y + 1] + pix2[y + 2] + pix2[y + 3] + 4 >> 3;
}
private void chromaPredBlk0(int comp, int x, int y, byte[] pred) {
int dc;
int predY = y & 0x7;
if (x != 0 && y != 0) {
dc = chromaPredTwo(this.leftRow[comp], this.topLine[comp], predY, x);
} else if (x != 0) {
dc = chromaPredOne(this.leftRow[comp], predY);
} else if (y != 0) {
dc = chromaPredOne(this.topLine[comp], x);
} else {
dc = 0;
}
for (int i = 0; i < pred.length; i++)
pred[i] = (byte)(pred[i] + dc);
}
private void chromaPredBlk1(int comp, int x, int y, byte[] pred) {
int dc;
int predY = y & 0x7;
if (y != 0) {
dc = chromaPredOne(this.topLine[comp], x + 4);
} else if (x != 0) {
dc = chromaPredOne(this.leftRow[comp], predY);
} else {
dc = 0;
}
for (int i = 0; i < pred.length; i++)
pred[i] = (byte)(pred[i] + dc);
}
private void chromaPredBlk2(int comp, int x, int y, byte[] pred) {
int dc;
int predY = y & 0x7;
if (x != 0) {
dc = chromaPredOne(this.leftRow[comp], predY + 4);
} else if (y != 0) {
dc = chromaPredOne(this.topLine[comp], x);
} else {
dc = 0;
}
for (int i = 0; i < pred.length; i++)
pred[i] = (byte)(pred[i] + dc);
}
private void chromaPredBlk3(int comp, int x, int y, byte[] pred) {
int dc;
int predY = y & 0x7;
if (x != 0 && y != 0) {
dc = chromaPredTwo(this.leftRow[comp], this.topLine[comp], predY + 4, x + 4);
} else if (x != 0) {
dc = chromaPredOne(this.leftRow[comp], predY + 4);
} else if (y != 0) {
dc = chromaPredOne(this.topLine[comp], x + 4);
} else {
dc = 0;
}
for (int i = 0; i < pred.length; i++)
pred[i] = (byte)(pred[i] + dc);
}
private void lumaDCPred(int x, int y, byte[][] pred) {
int dc;
if (x == 0 && y == 0) {
dc = 0;
} else if (y == 0) {
dc = ArrayUtil.sumByte(this.leftRow[0]) + 8 >> 4;
} else if (x == 0) {
dc = ArrayUtil.sumByte3(this.topLine[0], x, 16) + 8 >> 4;
} else {
dc = ArrayUtil.sumByte(this.leftRow[0]) + ArrayUtil.sumByte3(this.topLine[0], x, 16) + 16 >> 5;
}
for (int i = 0; i < pred.length; i++) {
for (int j = 0; j < (pred[i]).length; j++)
pred[i][j] = (byte)(pred[i][j] + dc);
}
}
private void transform(Picture pic, int comp, int[][] ac, byte[][] pred, int x, int y) {
for (int i = 0; i < ac.length; i++) {
int[] coeff = ac[i];
MBEncoderHelper.takeSubtract(pic.getPlaneData(comp), pic.getPlaneWidth(comp), pic.getPlaneHeight(comp), x + H264Const.BLK_X[i], y + H264Const.BLK_Y[i], coeff, pred[i], 4, 4);
CoeffTransformer.fdct4x4(coeff);
}
}
public int getPredMode(Picture pic, int mbX, int mbY) {
return 2;
}
public int getCbpChroma(Picture pic, int mbX, int mbY) {
return 2;
}
public int getCbpLuma(Picture pic, int mbX, int mbY) {
return 15;
}
}

View file

@ -0,0 +1,208 @@
package org.jcodec.codecs.h264.encode;
import java.util.Arrays;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.decode.BlockInterpolator;
import org.jcodec.codecs.h264.decode.CoeffTransformer;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.common.SaveRestore;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.model.Picture;
public class MBEncoderP16x16 implements SaveRestore {
private CAVLC[] cavlc;
private SeqParameterSet sps;
private Picture ref;
private MotionEstimator me;
private int[] mvTopX;
private int[] mvTopY;
private int mvLeftX;
private int mvLeftY;
private int mvTopLeftX;
private int mvTopLeftY;
private int[] mvTopXSave;
private int[] mvTopYSave;
private int mvLeftXSave;
private int mvLeftYSave;
private int mvTopLeftXSave;
private int mvTopLeftYSave;
private BlockInterpolator interpolator;
public MBEncoderP16x16(SeqParameterSet sps, Picture ref, CAVLC[] cavlc, MotionEstimator me) {
this.sps = sps;
this.cavlc = cavlc;
this.ref = ref;
this.me = me;
this.mvTopX = new int[sps.picWidthInMbsMinus1 + 1];
this.mvTopY = new int[sps.picWidthInMbsMinus1 + 1];
this.mvTopXSave = new int[sps.picWidthInMbsMinus1 + 1];
this.mvTopYSave = new int[sps.picWidthInMbsMinus1 + 1];
this.interpolator = new BlockInterpolator();
}
public void save() {
for (int i = 0; i < this.cavlc.length; i++)
this.cavlc[i].save();
System.arraycopy(this.mvTopX, 0, this.mvTopXSave, 0, this.mvTopX.length);
System.arraycopy(this.mvTopY, 0, this.mvTopYSave, 0, this.mvTopY.length);
this.mvLeftXSave = this.mvLeftX;
this.mvLeftYSave = this.mvLeftY;
this.mvTopLeftXSave = this.mvTopLeftX;
this.mvTopLeftYSave = this.mvTopLeftY;
}
public void restore() {
for (int i = 0; i < this.cavlc.length; i++)
this.cavlc[i].restore();
int[] tmp = this.mvTopX;
this.mvTopX = this.mvTopXSave;
this.mvTopXSave = tmp;
tmp = this.mvTopY;
this.mvTopY = this.mvTopYSave;
this.mvTopYSave = tmp;
this.mvLeftX = this.mvLeftXSave;
this.mvLeftY = this.mvLeftYSave;
this.mvTopLeftX = this.mvTopLeftXSave;
this.mvTopLeftY = this.mvTopLeftYSave;
}
public void encodeMacroblock(Picture pic, int mbX, int mbY, BitWriter out, EncodedMB outMB, EncodedMB leftOutMB, EncodedMB topOutMB, int qp, int qpDelta) {
if (this.sps.numRefFrames > 1) {
int refIdx = decideRef();
CAVLCWriter.writeTE(out, refIdx, this.sps.numRefFrames - 1);
}
boolean trAvb = (mbY > 0 && mbX < this.sps.picWidthInMbsMinus1);
boolean tlAvb = (mbX > 0 && mbY > 0);
int mvpx = median(this.mvLeftX, this.mvTopX[mbX], trAvb ? this.mvTopX[mbX + 1] : 0, tlAvb ? this.mvTopLeftX : 0, (mbX > 0), (mbY > 0), trAvb, tlAvb);
int mvpy = median(this.mvLeftY, this.mvTopY[mbX], trAvb ? this.mvTopY[mbX + 1] : 0, tlAvb ? this.mvTopLeftY : 0, (mbX > 0), (mbY > 0), trAvb, tlAvb);
int[] mv = mvEstimate(pic, mbX, mbY, mvpx, mvpy);
this.mvTopLeftX = this.mvTopX[mbX];
this.mvTopLeftY = this.mvTopY[mbX];
this.mvTopX[mbX] = mv[0];
this.mvTopY[mbX] = mv[1];
this.mvLeftX = mv[0];
this.mvLeftY = mv[1];
CAVLCWriter.writeSE(out, mv[0] - mvpx);
CAVLCWriter.writeSE(out, mv[1] - mvpy);
Picture mbRef = Picture.create(16, 16, this.sps.chromaFormatIdc);
int[][] mb = { new int[256], new int[64], new int[64] };
this.interpolator.getBlockLuma(this.ref, mbRef, 0, (mbX << 6) + mv[0], (mbY << 6) + mv[1], 16, 16);
BlockInterpolator.getBlockChroma(this.ref.getPlaneData(1), this.ref.getPlaneWidth(1), this.ref.getPlaneHeight(1),
mbRef.getPlaneData(1), 0, mbRef.getPlaneWidth(1), (mbX << 6) + mv[0], (mbY << 6) + mv[1], 8, 8);
BlockInterpolator.getBlockChroma(this.ref.getPlaneData(2), this.ref.getPlaneWidth(2), this.ref.getPlaneHeight(2),
mbRef.getPlaneData(2), 0, mbRef.getPlaneWidth(2), (mbX << 6) + mv[0], (mbY << 6) + mv[1], 8, 8);
MBEncoderHelper.takeSubtract(pic.getPlaneData(0), pic.getPlaneWidth(0), pic.getPlaneHeight(0), mbX << 4, mbY << 4, mb[0],
mbRef.getPlaneData(0), 16, 16);
MBEncoderHelper.takeSubtract(pic.getPlaneData(1), pic.getPlaneWidth(1), pic.getPlaneHeight(1), mbX << 3, mbY << 3, mb[1],
mbRef.getPlaneData(1), 8, 8);
MBEncoderHelper.takeSubtract(pic.getPlaneData(2), pic.getPlaneWidth(2), pic.getPlaneHeight(2), mbX << 3, mbY << 3, mb[2],
mbRef.getPlaneData(2), 8, 8);
int codedBlockPattern = getCodedBlockPattern();
CAVLCWriter.writeUE(out, H264Const.CODED_BLOCK_PATTERN_INTER_COLOR_INV[codedBlockPattern]);
CAVLCWriter.writeSE(out, qpDelta);
luma(pic, mb[0], mbX, mbY, out, qp, outMB.getNc());
chroma(pic, mb[1], mb[2], mbX, mbY, out, qp);
MBEncoderHelper.putBlk(outMB.getPixels().getPlaneData(0), mb[0], mbRef.getPlaneData(0), 4, 0, 0, 16, 16);
MBEncoderHelper.putBlk(outMB.getPixels().getPlaneData(1), mb[1], mbRef.getPlaneData(1), 3, 0, 0, 8, 8);
MBEncoderHelper.putBlk(outMB.getPixels().getPlaneData(2), mb[2], mbRef.getPlaneData(2), 3, 0, 0, 8, 8);
Arrays.fill(outMB.getMx(), mv[0]);
Arrays.fill(outMB.getMy(), mv[1]);
outMB.setType(MBType.P_16x16);
outMB.setQp(qp);
new MBDeblocker().deblockMBP(outMB, leftOutMB, topOutMB);
}
public int median(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb) {
if (!cAvb) {
c = d;
cAvb = dAvb;
}
if (aAvb && !bAvb && !cAvb) {
b = c = a;
bAvb = cAvb = aAvb;
}
a = aAvb ? a : 0;
b = bAvb ? b : 0;
c = cAvb ? c : 0;
return a + b + c - Math.min(Math.min(a, b), c) - Math.max(Math.max(a, b), c);
}
private int getCodedBlockPattern() {
return 47;
}
private int[] mvEstimate(Picture pic, int mbX, int mbY, int mvpx, int mvpy) {
byte[] patch = new byte[256];
MBEncoderHelper.take(pic.getPlaneData(0), pic.getPlaneWidth(0), pic.getPlaneHeight(0), mbX << 4, mbY << 4, patch, 16, 16);
return this.me.estimate(this.ref, patch, mbX, mbY, mvpx, mvpy);
}
private int decideRef() {
return 0;
}
private void luma(Picture pic, int[] pix, int mbX, int mbY, BitWriter out, int qp, int[] nc) {
int[][] ac = new int[16][16];
for (int j = 0; j < ac.length; j++) {
for (int k = 0; k < (H264Const.PIX_MAP_SPLIT_4x4[j]).length; k++)
ac[j][k] = pix[H264Const.PIX_MAP_SPLIT_4x4[j][k]];
CoeffTransformer.fdct4x4(ac[j]);
}
writeAC(0, mbX, mbY, out, mbX << 2, mbY << 2, ac, qp);
for (int i = 0; i < ac.length; i++) {
CoeffTransformer.dequantizeAC(ac[i], qp, null);
CoeffTransformer.idct4x4(ac[i]);
for (int k = 0; k < (H264Const.PIX_MAP_SPLIT_4x4[i]).length; k++)
pix[H264Const.PIX_MAP_SPLIT_4x4[i][k]] = ac[i][k];
}
}
private void chroma(Picture pic, int[] pix1, int[] pix2, int mbX, int mbY, BitWriter out, int qp) {
int[][] ac1 = new int[4][16];
int[][] ac2 = new int[4][16];
for (int m = 0; m < ac1.length; m++) {
for (int n = 0; n < (H264Const.PIX_MAP_SPLIT_2x2[m]).length; n++)
ac1[m][n] = pix1[H264Const.PIX_MAP_SPLIT_2x2[m][n]];
}
for (int k = 0; k < ac2.length; k++) {
for (int n = 0; n < (H264Const.PIX_MAP_SPLIT_2x2[k]).length; n++)
ac2[k][n] = pix2[H264Const.PIX_MAP_SPLIT_2x2[k][n]];
}
MBEncoderI16x16.chromaResidual(pic, mbX, mbY, out, qp, ac1, ac2, this.cavlc[1], this.cavlc[2], MBType.P_16x16, MBType.P_16x16);
for (int j = 0; j < ac1.length; j++) {
for (int n = 0; n < (H264Const.PIX_MAP_SPLIT_2x2[j]).length; n++)
pix1[H264Const.PIX_MAP_SPLIT_2x2[j][n]] = ac1[j][n];
}
for (int i = 0; i < ac2.length; i++) {
for (int n = 0; n < (H264Const.PIX_MAP_SPLIT_2x2[i]).length; n++)
pix2[H264Const.PIX_MAP_SPLIT_2x2[i][n]] = ac2[i][n];
}
}
private void writeAC(int comp, int mbX, int mbY, BitWriter out, int mbLeftBlk, int mbTopBlk, int[][] ac, int qp) {
for (int i = 0; i < ac.length; i++) {
int blkI = H264Const.BLK_INV_MAP[i];
CoeffTransformer.quantizeAC(ac[blkI], qp);
this.cavlc[comp].writeACBlock(out, mbLeftBlk + H264Const.MB_BLK_OFF_LEFT[i], mbTopBlk + H264Const.MB_BLK_OFF_TOP[i], MBType.P_16x16, MBType.P_16x16, ac[blkI], H264Const.totalZeros16, 0, 16, CoeffTransformer.zigzag4x4);
}
}
}

View file

@ -0,0 +1,59 @@
package org.jcodec.codecs.h264.encode;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class MotionEstimator {
private int maxSearchRange;
public MotionEstimator(int maxSearchRange) {
this.maxSearchRange = maxSearchRange;
}
public int[] estimate(Picture ref, byte[] patch, int mbX, int mbY, int mvpx, int mvpy) {
byte[] searchPatch = new byte[(this.maxSearchRange * 2 + 16) * (this.maxSearchRange * 2 + 16)];
int startX = mbX << 4;
int startY = mbY << 4;
int patchTlX = Math.max(startX - this.maxSearchRange, 0);
int patchTlY = Math.max(startY - this.maxSearchRange, 0);
int patchBrX = Math.min(startX + this.maxSearchRange + 16, ref.getPlaneWidth(0));
int patchBrY = Math.min(startY + this.maxSearchRange + 16, ref.getPlaneHeight(0));
int centerX = startX - patchTlX;
int centerY = startY - patchTlY;
int patchW = patchBrX - patchTlX;
int patchH = patchBrY - patchTlY;
MBEncoderHelper.takeSafe(ref.getPlaneData(0), ref.getPlaneWidth(0), ref.getPlaneHeight(0), patchTlX, patchTlY, searchPatch, patchW, patchH);
int bestMvX = centerX, bestMvY = centerY;
int bestScore = sad(searchPatch, patchW, patch, bestMvX, bestMvY);
for (int i = 0; i < this.maxSearchRange; i++) {
int score1 = (bestMvX > 0) ? sad(searchPatch, patchW, patch, bestMvX - 1, bestMvY) : Integer.MAX_VALUE;
int score2 = (bestMvX < patchW - 1) ? sad(searchPatch, patchW, patch, bestMvX + 1, bestMvY) : Integer.MAX_VALUE;
int score3 = (bestMvY > 0) ? sad(searchPatch, patchW, patch, bestMvX, bestMvY - 1) : Integer.MAX_VALUE;
int score4 = (bestMvY < patchH - 1) ? sad(searchPatch, patchW, patch, bestMvX, bestMvY + 1) : Integer.MAX_VALUE;
int min = Math.min(Math.min(Math.min(score1, score2), score3), score4);
if (min > bestScore)
break;
bestScore = min;
if (score1 == min) {
bestMvX--;
} else if (score2 == min) {
bestMvX++;
} else if (score3 == min) {
bestMvY--;
} else {
bestMvY++;
}
}
return new int[] { bestMvX - centerX << 2, bestMvY - centerY << 2 };
}
private int sad(byte[] big, int bigStride, byte[] small, int offX, int offY) {
int score = 0, bigOff = offY * bigStride + offX, smallOff = 0;
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++, bigOff++, smallOff++)
score += MathUtil.abs(big[bigOff] - small[smallOff]);
bigOff += bigStride - 16;
}
return score;
}
}

View file

@ -0,0 +1,12 @@
package org.jcodec.codecs.h264.encode;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.model.Size;
public interface RateControl {
int startPicture(Size paramSize, int paramInt, SliceType paramSliceType);
int initialQpDelta();
int accept(int paramInt);
}

View file

@ -0,0 +1,640 @@
package org.jcodec.codecs.h264.io;
import org.jcodec.codecs.common.biari.MDecoder;
import org.jcodec.codecs.common.biari.MEncoder;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils2;
import org.jcodec.codecs.h264.decode.CABACContst;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.tools.MathUtil;
public class CABAC {
private int chromaPredModeLeft;
private int[] chromaPredModeTop;
private int prevMbQpDelta;
private int prevCBP;
private int[][] codedBlkLeft;
private int[][] codedBlkTop;
private int[] codedBlkDCLeft;
private int[][] codedBlkDCTop;
private int[][] refIdxLeft;
private int[][] refIdxTop;
private boolean skipFlagLeft;
private boolean[] skipFlagsTop;
private int[][][] mvdTop;
private int[][][] mvdLeft;
public int[] tmp;
public static final class BlockType {
public static final BlockType LUMA_16_DC = new BlockType(85, 105, 166, 277, 338, 227, 0);
public static final BlockType LUMA_15_AC = new BlockType(89, 120, 181, 292, 353, 237, 0);
public static final BlockType LUMA_16 = new BlockType(93, 134, 195, 306, 367, 247, 0);
public static final BlockType CHROMA_DC = new BlockType(97, 149, 210, 321, 382, 257, 1);
public static final BlockType CHROMA_AC = new BlockType(101, 152, 213, 324, 385, 266, 0);
public static final BlockType LUMA_64 = new BlockType(1012, 402, 417, 436, 451, 426, 0);
public static final BlockType CB_16_DC = new BlockType(460, 484, 572, 776, 864, 952, 0);
public static final BlockType CB_15x16_AC = new BlockType(464, 499, 587, 791, 879, 962, 0);
public static final BlockType CB_16 = new BlockType(468, 513, 601, 805, 893, 972, 0);
public static final BlockType CB_64 = new BlockType(1016, 660, 690, 675, 699, 708, 0);
public static final BlockType CR_16_DC = new BlockType(472, 528, 616, 820, 908, 982, 0);
public static final BlockType CR_15x16_AC = new BlockType(476, 543, 631, 835, 923, 992, 0);
public static final BlockType CR_16 = new BlockType(480, 557, 645, 849, 937, 1002, 0);
public static final BlockType CR_64 = new BlockType(1020, 718, 748, 733, 757, 766, 0);
public int codedBlockCtxOff;
public int sigCoeffFlagCtxOff;
public int lastSigCoeffCtxOff;
public int sigCoeffFlagFldCtxOff;
public int lastSigCoeffFldCtxOff;
public int coeffAbsLevelCtxOff;
public int coeffAbsLevelAdjust;
private BlockType(int codecBlockCtxOff, int sigCoeffCtxOff, int lastSigCoeffCtxOff, int sigCoeffFlagFldCtxOff, int lastSigCoeffFldCtxOff, int coeffAbsLevelCtxOff, int coeffAbsLevelAdjust) {
this.codedBlockCtxOff = codecBlockCtxOff;
this.sigCoeffFlagCtxOff = sigCoeffCtxOff;
this.lastSigCoeffCtxOff = lastSigCoeffCtxOff;
this.sigCoeffFlagFldCtxOff = sigCoeffFlagFldCtxOff;
this.lastSigCoeffFldCtxOff = sigCoeffFlagFldCtxOff;
this.coeffAbsLevelCtxOff = coeffAbsLevelCtxOff;
this.coeffAbsLevelAdjust = coeffAbsLevelAdjust;
}
}
public CABAC(int mbWidth) {
this.tmp = new int[16];
this.chromaPredModeLeft = 0;
this.chromaPredModeTop = new int[mbWidth];
this.codedBlkLeft = new int[][] { new int[4], new int[2], new int[2] };
this.codedBlkTop = new int[][] { new int[mbWidth << 2], new int[mbWidth << 1], new int[mbWidth << 1] };
this.codedBlkDCLeft = new int[3];
this.codedBlkDCTop = new int[3][mbWidth];
this.refIdxLeft = new int[2][4];
this.refIdxTop = new int[2][mbWidth << 2];
this.skipFlagsTop = new boolean[mbWidth];
this.mvdTop = new int[2][2][mbWidth << 2];
this.mvdLeft = new int[2][2][4];
}
public int readCoeffs(MDecoder decoder, BlockType blockType, int[] out, int first, int num, int[] reorder, int[] scMapping, int[] lscMapping) {
boolean[] sigCoeff = new boolean[num];
int numCoeff;
for (numCoeff = 0; numCoeff < num - 1; numCoeff++) {
sigCoeff[numCoeff] = (decoder.decodeBin(blockType.sigCoeffFlagCtxOff + scMapping[numCoeff]) == 1);
if (sigCoeff[numCoeff] && decoder.decodeBin(blockType.lastSigCoeffCtxOff + lscMapping[numCoeff]) == 1)
break;
}
sigCoeff[numCoeff++] = true;
int numGt1 = 0, numEq1 = 0;
for (int j = numCoeff - 1; j >= 0; j--) {
if (sigCoeff[j]) {
int absLev = readCoeffAbsLevel(decoder, blockType, numGt1, numEq1);
if (absLev == 0) {
numEq1++;
} else {
numGt1++;
}
out[reorder[j + first]] = MathUtil.toSigned(absLev + 1, -decoder.decodeBinBypass());
}
}
return numGt1 + numEq1;
}
private int readCoeffAbsLevel(MDecoder decoder, BlockType blockType, int numDecodAbsLevelGt1, int numDecodAbsLevelEq1) {
int incB0 = (numDecodAbsLevelGt1 != 0) ? 0 : Math.min(4, 1 + numDecodAbsLevelEq1);
int incBN = 5 + Math.min(4 - blockType.coeffAbsLevelAdjust, numDecodAbsLevelGt1);
int b = decoder.decodeBin(blockType.coeffAbsLevelCtxOff + incB0);
int val;
for (val = 0; b != 0 && val < 13; val++)
b = decoder.decodeBin(blockType.coeffAbsLevelCtxOff + incBN);
val += b;
if (val == 14) {
int log = -2, add = 0, sum = 0;
do {
log++;
b = decoder.decodeBinBypass();
} while (b != 0);
for (; log >= 0; log--) {
add |= decoder.decodeBinBypass() << log;
sum += 1 << log;
}
val += add + sum;
}
return val;
}
public void writeCoeffs(MEncoder encoder, BlockType blockType, int[] _out, int first, int num, int[] reorder) {
for (int i = 0; i < num; i++)
this.tmp[i] = _out[reorder[first + i]];
int numCoeff = 0;
for (int m = 0; m < num; m++) {
if (this.tmp[m] != 0)
numCoeff = m + 1;
}
for (int k = 0; k < Math.min(numCoeff, num - 1); k++) {
if (this.tmp[k] != 0) {
encoder.encodeBin(blockType.sigCoeffFlagCtxOff + k, 1);
encoder.encodeBin(blockType.lastSigCoeffCtxOff + k, (k == numCoeff - 1) ? 1 : 0);
} else {
encoder.encodeBin(blockType.sigCoeffFlagCtxOff + k, 0);
}
}
int numGt1 = 0, numEq1 = 0;
for (int j = numCoeff - 1; j >= 0; j--) {
if (this.tmp[j] != 0) {
int absLev = MathUtil.abs(this.tmp[j]) - 1;
writeCoeffAbsLevel(encoder, blockType, numGt1, numEq1, absLev);
if (absLev == 0) {
numEq1++;
} else {
numGt1++;
}
encoder.encodeBinBypass(MathUtil.sign(this.tmp[j]));
}
}
}
private void writeCoeffAbsLevel(MEncoder encoder, BlockType blockType, int numDecodAbsLevelGt1, int numDecodAbsLevelEq1, int absLev) {
int incB0 = (numDecodAbsLevelGt1 != 0) ? 0 : Math.min(4, 1 + numDecodAbsLevelEq1);
int incBN = 5 + Math.min(4 - blockType.coeffAbsLevelAdjust, numDecodAbsLevelGt1);
if (absLev == 0) {
encoder.encodeBin(blockType.coeffAbsLevelCtxOff + incB0, 0);
} else {
encoder.encodeBin(blockType.coeffAbsLevelCtxOff + incB0, 1);
if (absLev < 14) {
for (int i = 1; i < absLev; i++)
encoder.encodeBin(blockType.coeffAbsLevelCtxOff + incBN, 1);
encoder.encodeBin(blockType.coeffAbsLevelCtxOff + incBN, 0);
} else {
for (int i = 1; i < 14; i++)
encoder.encodeBin(blockType.coeffAbsLevelCtxOff + incBN, 1);
absLev -= 14;
int sufLen, pow;
for (sufLen = 0, pow = 1; absLev >= pow; pow = 1 << ++sufLen) {
encoder.encodeBinBypass(1);
absLev -= pow;
}
encoder.encodeBinBypass(0);
for (; --sufLen >= 0; sufLen--)
encoder.encodeBinBypass(absLev >> sufLen & 0x1);
}
}
}
public void initModels(int[][] cm, SliceType sliceType, int cabacIdc, int sliceQp) {
int[] tabA = sliceType.isIntra() ? CABACContst.cabac_context_init_I_A :
CABACContst.cabac_context_init_PB_A[cabacIdc];
int[] tabB = sliceType.isIntra() ? CABACContst.cabac_context_init_I_B :
CABACContst.cabac_context_init_PB_B[cabacIdc];
for (int i = 0; i < 1024; i++) {
int preCtxState = MathUtil.clip((tabA[i] * MathUtil.clip(sliceQp, 0, 51) >> 4) + tabB[i], 1, 126);
if (preCtxState <= 63) {
cm[0][i] = 63 - preCtxState;
cm[1][i] = 0;
} else {
cm[0][i] = preCtxState - 64;
cm[1][i] = 1;
}
}
}
public int readMBTypeI(MDecoder decoder, MBType left, MBType top, boolean leftAvailable, boolean topAvailable) {
int ctx = 3;
ctx += (!leftAvailable || left == MBType.I_NxN) ? 0 : 1;
ctx += (!topAvailable || top == MBType.I_NxN) ? 0 : 1;
if (decoder.decodeBin(ctx) == 0)
return 0;
return (decoder.decodeFinalBin() == 1) ? 25 : (1 + readMBType16x16(decoder));
}
private int readMBType16x16(MDecoder decoder) {
int type = decoder.decodeBin(6) * 12;
if (decoder.decodeBin(7) == 0)
return type + (decoder.decodeBin(9) << 1) + decoder.decodeBin(10);
return type + (decoder.decodeBin(8) << 2) + (decoder.decodeBin(9) << 1) + decoder.decodeBin(10) + 4;
}
public int readMBTypeP(MDecoder decoder) {
if (decoder.decodeBin(14) == 1)
return 5 + readIntraP(decoder, 17);
if (decoder.decodeBin(15) == 0)
return (decoder.decodeBin(16) == 0) ? 0 : 3;
return (decoder.decodeBin(17) == 0) ? 2 : 1;
}
private int readIntraP(MDecoder decoder, int ctxOff) {
if (decoder.decodeBin(ctxOff) == 0)
return 0;
return (decoder.decodeFinalBin() == 1) ? 25 : (1 + readMBType16x16P(decoder, ctxOff));
}
private int readMBType16x16P(MDecoder decoder, int ctxOff) {
ctxOff++;
int type = decoder.decodeBin(ctxOff) * 12;
ctxOff++;
if (decoder.decodeBin(ctxOff) == 0) {
ctxOff++;
return type + (decoder.decodeBin(ctxOff) << 1) + decoder.decodeBin(ctxOff);
}
return type + (decoder.decodeBin(ctxOff) << 2) + (decoder.decodeBin(ctxOff + 1) << 1) +
decoder.decodeBin(ctxOff + 1) + 4;
}
public int readMBTypeB(MDecoder mDecoder, MBType left, MBType top, boolean leftAvailable, boolean topAvailable) {
int ctx = 27;
ctx += (!leftAvailable || left == null || left == MBType.B_Direct_16x16) ? 0 : 1;
ctx += (!topAvailable || top == null || top == MBType.B_Direct_16x16) ? 0 : 1;
if (mDecoder.decodeBin(ctx) == 0)
return 0;
if (mDecoder.decodeBin(30) == 0)
return 1 + mDecoder.decodeBin(32);
int b1 = mDecoder.decodeBin(31);
if (b1 == 0)
return 3 + (mDecoder.decodeBin(32) << 2 | mDecoder.decodeBin(32) << 1 | mDecoder.decodeBin(32));
if (mDecoder.decodeBin(32) == 0)
return 12 + (mDecoder.decodeBin(32) << 2 | mDecoder.decodeBin(32) << 1 | mDecoder.decodeBin(32));
switch ((mDecoder.decodeBin(32) << 1) + mDecoder.decodeBin(32)) {
case 0:
return 20 + mDecoder.decodeBin(32);
case 1:
return 23 + readIntraP(mDecoder, 32);
case 2:
return 11;
case 3:
return 22;
}
return 0;
}
public void writeMBTypeI(MEncoder encoder, MBType left, MBType top, boolean leftAvailable, boolean topAvailable, int mbType) {
int ctx = 3;
ctx += (!leftAvailable || left == MBType.I_NxN) ? 0 : 1;
ctx += (!topAvailable || top == MBType.I_NxN) ? 0 : 1;
if (mbType == 0) {
encoder.encodeBin(ctx, 0);
} else {
encoder.encodeBin(ctx, 1);
if (mbType == 25) {
encoder.encodeBinFinal(1);
} else {
encoder.encodeBinFinal(0);
writeMBType16x16(encoder, mbType - 1);
}
}
}
private void writeMBType16x16(MEncoder encoder, int mbType) {
if (mbType < 12) {
encoder.encodeBin(6, 0);
} else {
encoder.encodeBin(6, 1);
mbType -= 12;
}
if (mbType < 4) {
encoder.encodeBin(7, 0);
encoder.encodeBin(9, mbType >> 1);
encoder.encodeBin(10, mbType & 0x1);
} else {
mbType -= 4;
encoder.encodeBin(7, 1);
encoder.encodeBin(8, mbType >> 2);
encoder.encodeBin(9, mbType >> 1 & 0x1);
encoder.encodeBin(10, mbType & 0x1);
}
}
public int readMBQpDelta(MDecoder decoder, MBType prevMbType) {
int ctx = 60;
ctx += (
prevMbType == null || prevMbType == MBType.I_PCM || (prevMbType != MBType.I_16x16 && this.prevCBP == 0) || this.prevMbQpDelta == 0) ? 0 : 1;
int val = 0;
if (decoder.decodeBin(ctx) == 1) {
val++;
if (decoder.decodeBin(62) == 1) {
val++;
while (decoder.decodeBin(63) == 1)
val++;
}
}
this.prevMbQpDelta = H264Utils2.golomb2Signed(val);
return this.prevMbQpDelta;
}
public void writeMBQpDelta(MEncoder encoder, MBType prevMbType, int mbQpDelta) {
int ctx = 60;
ctx += (
prevMbType == null || prevMbType == MBType.I_PCM || (prevMbType != MBType.I_16x16 && this.prevCBP == 0) || this.prevMbQpDelta == 0) ? 0 : 1;
this.prevMbQpDelta = mbQpDelta;
if (mbQpDelta-- == 0) {
encoder.encodeBin(ctx, 0);
} else {
encoder.encodeBin(ctx, 1);
if (mbQpDelta-- == 0) {
encoder.encodeBin(62, 0);
} else {
while (mbQpDelta-- > 0)
encoder.encodeBin(63, 1);
encoder.encodeBin(63, 0);
}
}
}
public int readIntraChromaPredMode(MDecoder decoder, int mbX, MBType left, MBType top, boolean leftAvailable, boolean topAvailable) {
int mode;
int ctx = 64;
ctx += (!leftAvailable || left == null || !left.isIntra() || this.chromaPredModeLeft == 0) ? 0 : 1;
ctx += (!topAvailable || top == null || !top.isIntra() || this.chromaPredModeTop[mbX] == 0) ? 0 : 1;
if (decoder.decodeBin(ctx) == 0) {
mode = 0;
} else if (decoder.decodeBin(67) == 0) {
mode = 1;
} else if (decoder.decodeBin(67) == 0) {
mode = 2;
} else {
mode = 3;
}
this.chromaPredModeLeft = this.chromaPredModeTop[mbX] = mode;
return mode;
}
public void writeIntraChromaPredMode(MEncoder encoder, int mbX, MBType left, MBType top, boolean leftAvailable, boolean topAvailable, int mode) {
int ctx = 64;
ctx += (!leftAvailable || !left.isIntra() || this.chromaPredModeLeft == 0) ? 0 : 1;
ctx += (!topAvailable || !top.isIntra() || this.chromaPredModeTop[mbX] == 0) ? 0 : 1;
encoder.encodeBin(ctx, (mode-- == 0) ? 0 : 1);
for (int i = 0; mode >= 0 && i < 2; i++)
encoder.encodeBin(67, (mode-- == 0) ? 0 : 1);
this.chromaPredModeLeft = this.chromaPredModeTop[mbX] = mode;
}
public int condTerm(MBType mbCur, boolean nAvb, MBType mbN, boolean nBlkAvb, int cbpN) {
if (!nAvb)
return mbCur.isIntra() ? 1 : 0;
if (mbN == MBType.I_PCM)
return 1;
if (!nBlkAvb)
return 0;
return cbpN;
}
public int readCodedBlockFlagLumaDC(MDecoder decoder, int mbX, MBType left, MBType top, boolean leftAvailable, boolean topAvailable, MBType cur) {
int tLeft = condTerm(cur, leftAvailable, left, (left == MBType.I_16x16), this.codedBlkDCLeft[0]);
int tTop = condTerm(cur, topAvailable, top, (top == MBType.I_16x16), this.codedBlkDCTop[0][mbX]);
int decoded = decoder.decodeBin(BlockType.LUMA_16_DC.codedBlockCtxOff + tLeft + 2 * tTop);
this.codedBlkDCLeft[0] = decoded;
this.codedBlkDCTop[0][mbX] = decoded;
return decoded;
}
public int readCodedBlockFlagChromaDC(MDecoder decoder, int mbX, int comp, MBType left, MBType top, boolean leftAvailable, boolean topAvailable, int leftCBPChroma, int topCBPChroma, MBType cur) {
int tLeft = condTerm(cur, leftAvailable, left, (left != null && leftCBPChroma != 0), this.codedBlkDCLeft[comp]);
int tTop = condTerm(cur, topAvailable, top, (top != null && topCBPChroma != 0), this.codedBlkDCTop[comp][mbX]);
int decoded = decoder.decodeBin(BlockType.CHROMA_DC.codedBlockCtxOff + tLeft + 2 * tTop);
this.codedBlkDCLeft[comp] = decoded;
this.codedBlkDCTop[comp][mbX] = decoded;
return decoded;
}
public int readCodedBlockFlagLumaAC(MDecoder decoder, BlockType blkType, int blkX, int blkY, int comp, MBType left, MBType top, boolean leftAvailable, boolean topAvailable, int leftCBPLuma, int topCBPLuma, int curCBPLuma, MBType cur) {
int tLeft, tTop;
int blkOffLeft = blkX & 0x3, blkOffTop = blkY & 0x3;
if (blkOffLeft == 0) {
tLeft = condTerm(cur, leftAvailable, left, (left != null && left != MBType.I_PCM && cbp(leftCBPLuma, 3, blkOffTop)), this.codedBlkLeft[comp][blkOffTop]);
} else {
tLeft = condTerm(cur, true, cur, cbp(curCBPLuma, blkOffLeft - 1, blkOffTop), this.codedBlkLeft[comp][blkOffTop]);
}
if (blkOffTop == 0) {
tTop = condTerm(cur, topAvailable, top, (top != null && top != MBType.I_PCM && cbp(topCBPLuma, blkOffLeft, 3)), this.codedBlkTop[comp][blkX]);
} else {
tTop = condTerm(cur, true, cur, cbp(curCBPLuma, blkOffLeft, blkOffTop - 1), this.codedBlkTop[comp][blkX]);
}
int decoded = decoder.decodeBin(blkType.codedBlockCtxOff + tLeft + 2 * tTop);
this.codedBlkLeft[comp][blkOffTop] = decoded;
this.codedBlkTop[comp][blkX] = decoded;
return decoded;
}
public int readCodedBlockFlagLuma64(MDecoder decoder, int blkX, int blkY, int comp, MBType left, MBType top, boolean leftAvailable, boolean topAvailable, int leftCBPLuma, int topCBPLuma, int curCBPLuma, MBType cur, boolean is8x8Left, boolean is8x8Top) {
int tLeft, tTop;
int blkOffLeft = blkX & 0x3, blkOffTop = blkY & 0x3;
if (blkOffLeft == 0) {
tLeft = condTerm(cur, leftAvailable, left, (left != null && left != MBType.I_PCM && is8x8Left &&
cbp(leftCBPLuma, 3, blkOffTop)), this.codedBlkLeft[comp][blkOffTop]);
} else {
tLeft = condTerm(cur, true, cur, cbp(curCBPLuma, blkOffLeft - 1, blkOffTop), this.codedBlkLeft[comp][blkOffTop]);
}
if (blkOffTop == 0) {
tTop = condTerm(cur, topAvailable, top, (top != null && top != MBType.I_PCM && is8x8Top &&
cbp(topCBPLuma, blkOffLeft, 3)), this.codedBlkTop[comp][blkX]);
} else {
tTop = condTerm(cur, true, cur, cbp(curCBPLuma, blkOffLeft, blkOffTop - 1), this.codedBlkTop[comp][blkX]);
}
int decoded = decoder.decodeBin(BlockType.LUMA_64.codedBlockCtxOff + tLeft + 2 * tTop);
this.codedBlkLeft[comp][blkOffTop] = decoded;
this.codedBlkTop[comp][blkX] = decoded;
return decoded;
}
private boolean cbp(int cbpLuma, int blkX, int blkY) {
int x8x8 = (blkY & 0x2) + (blkX >> 1);
return ((cbpLuma >> x8x8 & 0x1) == 1);
}
public int readCodedBlockFlagChromaAC(MDecoder decoder, int blkX, int blkY, int comp, MBType left, MBType top, boolean leftAvailable, boolean topAvailable, int leftCBPChroma, int topCBPChroma, MBType cur) {
int tLeft, tTop;
int blkOffLeft = blkX & 0x1, blkOffTop = blkY & 0x1;
if (blkOffLeft == 0) {
tLeft = condTerm(cur, leftAvailable, left, (left != null && left != MBType.I_PCM && (leftCBPChroma & 0x2) != 0), this.codedBlkLeft[comp][blkOffTop]);
} else {
tLeft = condTerm(cur, true, cur, true, this.codedBlkLeft[comp][blkOffTop]);
}
if (blkOffTop == 0) {
tTop = condTerm(cur, topAvailable, top, (top != null && top != MBType.I_PCM && (topCBPChroma & 0x2) != 0), this.codedBlkTop[comp][blkX]);
} else {
tTop = condTerm(cur, true, cur, true, this.codedBlkTop[comp][blkX]);
}
int decoded = decoder.decodeBin(BlockType.CHROMA_AC.codedBlockCtxOff + tLeft + 2 * tTop);
this.codedBlkLeft[comp][blkOffTop] = decoded;
this.codedBlkTop[comp][blkX] = decoded;
return decoded;
}
public boolean prev4x4PredModeFlag(MDecoder decoder) {
return (decoder.decodeBin(68) == 1);
}
public int rem4x4PredMode(MDecoder decoder) {
return decoder.decodeBin(69) | decoder.decodeBin(69) << 1 | decoder.decodeBin(69) << 2;
}
public int codedBlockPatternIntra(MDecoder mDecoder, boolean leftAvailable, boolean topAvailable, int cbpLeft, int cbpTop, MBType mbLeft, MBType mbTop) {
int cbp0 = mDecoder.decodeBin(73 + _condTerm(leftAvailable, mbLeft, cbpLeft >> 1 & 0x1) + 2 *
_condTerm(topAvailable, mbTop, cbpTop >> 2 & 0x1));
int cbp1 = mDecoder.decodeBin(73 + 1 - cbp0 + 2 * _condTerm(topAvailable, mbTop, cbpTop >> 3 & 0x1));
int cbp2 = mDecoder.decodeBin(73 + _condTerm(leftAvailable, mbLeft, cbpLeft >> 3 & 0x1) + 2 * (1 - cbp0));
int cbp3 = mDecoder.decodeBin(73 + 1 - cbp2 + 2 * (1 - cbp1));
int cr0 = mDecoder.decodeBin(77 + condTermCr0(leftAvailable, mbLeft, cbpLeft >> 4) + 2 *
condTermCr0(topAvailable, mbTop, cbpTop >> 4));
int cr1 = (cr0 != 0) ? mDecoder.decodeBin(81 + condTermCr1(leftAvailable, mbLeft, cbpLeft >> 4) + 2 *
condTermCr1(topAvailable, mbTop, cbpTop >> 4)) : 0;
return cbp0 | cbp1 << 1 | cbp2 << 2 | cbp3 << 3 | cr0 << 4 | cr1 << 5;
}
private int condTermCr0(boolean avb, MBType mbt, int cbpChroma) {
return (avb && (mbt == MBType.I_PCM || (mbt != null && cbpChroma != 0))) ? 1 : 0;
}
private int condTermCr1(boolean avb, MBType mbt, int cbpChroma) {
return (avb && (mbt == MBType.I_PCM || (mbt != null && (cbpChroma & 0x2) != 0))) ? 1 : 0;
}
private int _condTerm(boolean avb, MBType mbt, int cbp) {
return (!avb || mbt == MBType.I_PCM || (mbt != null && cbp == 1)) ? 0 : 1;
}
public void setPrevCBP(int prevCBP) {
this.prevCBP = prevCBP;
}
public int readMVD(MDecoder decoder, int comp, boolean leftAvailable, boolean topAvailable, MBType leftType, MBType topType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred curPred, int mbX, int partX, int partY, int partW, int partH, int list) {
int ctx = (comp == 0) ? 40 : 47;
int partAbsX = (mbX << 2) + partX;
boolean predEqA = (leftPred != null && leftPred != H264Const.PartPred.Direct && (leftPred == H264Const.PartPred.Bi || leftPred == curPred || (curPred == H264Const.PartPred.Bi &&
H264Const.usesList(leftPred, list))));
boolean predEqB = (topPred != null && topPred != H264Const.PartPred.Direct && (topPred == H264Const.PartPred.Bi || topPred == curPred || (curPred == H264Const.PartPred.Bi &&
H264Const.usesList(topPred, list))));
int absMvdComp = (!leftAvailable || leftType == null || leftType.isIntra() || !predEqA) ? 0 :
Math.abs(this.mvdLeft[list][comp][partY]);
absMvdComp += (!topAvailable || topType == null || topType.isIntra() || !predEqB) ? 0 :
Math.abs(this.mvdTop[list][comp][partAbsX]);
int b = decoder.decodeBin(ctx + ((absMvdComp < 3) ? 0 : ((absMvdComp > 32) ? 2 : 1)));
int val;
for (val = 0; b != 0 && val < 8; val++)
b = decoder.decodeBin(Math.min(ctx + val + 3, ctx + 6));
val += b;
if (val != 0) {
if (val == 9) {
int log = 2, add = 0, sum = 0, leftover = 0;
do {
sum += leftover;
log++;
b = decoder.decodeBinBypass();
leftover = 1 << log;
} while (b != 0);
log--;
for (; log >= 0; log--)
add |= decoder.decodeBinBypass() << log;
val += add + sum;
}
val = MathUtil.toSigned(val, -decoder.decodeBinBypass());
}
for (int j = 0; j < partW; j++)
this.mvdTop[list][comp][partAbsX + j] = val;
for (int i = 0; i < partH; i++)
this.mvdLeft[list][comp][partY + i] = val;
return val;
}
public int readRefIdx(MDecoder mDecoder, boolean leftAvailable, boolean topAvailable, MBType leftType, MBType topType, H264Const.PartPred leftPred, H264Const.PartPred topPred, H264Const.PartPred curPred, int mbX, int partX, int partY, int partW, int partH, int list) {
int val;
int partAbsX = (mbX << 2) + partX;
boolean predEqA = (leftPred != null && leftPred != H264Const.PartPred.Direct && (leftPred == H264Const.PartPred.Bi || leftPred == curPred || (curPred == H264Const.PartPred.Bi &&
H264Const.usesList(leftPred, list))));
boolean predEqB = (topPred != null && topPred != H264Const.PartPred.Direct && (topPred == H264Const.PartPred.Bi || topPred == curPred || (curPred == H264Const.PartPred.Bi &&
H264Const.usesList(topPred, list))));
int ctA = (!leftAvailable || leftType == null || leftType.isIntra() || !predEqA || this.refIdxLeft[list][partY] == 0) ? 0 :
1;
int ctB = (!topAvailable || topType == null || topType.isIntra() || !predEqB || this.refIdxTop[list][partAbsX] == 0) ? 0 :
1;
int b0 = mDecoder.decodeBin(54 + ctA + 2 * ctB);
if (b0 == 0) {
val = 0;
} else {
int b1 = mDecoder.decodeBin(58);
if (b1 == 0) {
val = 1;
} else {
for (val = 2; mDecoder.decodeBin(59) == 1; val++);
}
}
for (int j = 0; j < partW; j++)
this.refIdxTop[list][partAbsX + j] = val;
for (int i = 0; i < partH; i++)
this.refIdxLeft[list][partY + i] = val;
return val;
}
public boolean readMBSkipFlag(MDecoder mDecoder, SliceType slType, boolean leftAvailable, boolean topAvailable, int mbX) {
int base = (slType == SliceType.P) ? 11 : 24;
boolean ret = (mDecoder.decodeBin(base + ((leftAvailable && !this.skipFlagLeft) ? 1 : 0) + ((
topAvailable && !this.skipFlagsTop[mbX]) ? 1 : 0)) == 1);
this.skipFlagLeft = this.skipFlagsTop[mbX] = ret;
return ret;
}
public int readSubMbTypeP(MDecoder mDecoder) {
if (mDecoder.decodeBin(21) == 1)
return 0;
if (mDecoder.decodeBin(22) == 0)
return 1;
if (mDecoder.decodeBin(23) == 1)
return 2;
return 3;
}
public int readSubMbTypeB(MDecoder mDecoder) {
if (mDecoder.decodeBin(36) == 0)
return 0;
if (mDecoder.decodeBin(37) == 0)
return 1 + mDecoder.decodeBin(39);
if (mDecoder.decodeBin(38) == 0)
return 3 + (mDecoder.decodeBin(39) << 1) + mDecoder.decodeBin(39);
if (mDecoder.decodeBin(39) == 0)
return 7 + (mDecoder.decodeBin(39) << 1) + mDecoder.decodeBin(39);
return 11 + mDecoder.decodeBin(39);
}
public boolean readTransform8x8Flag(MDecoder mDecoder, boolean leftAvailable, boolean topAvailable, MBType leftType, MBType topType, boolean is8x8Left, boolean is8x8Top) {
int ctx = 399 + ((leftAvailable && leftType != null && is8x8Left) ? 1 : 0) + ((
topAvailable && topType != null && is8x8Top) ? 1 : 0);
return (mDecoder.decodeBin(ctx) == 1);
}
public void setCodedBlock(int blkX, int blkY) {
this.codedBlkTop[0][blkX] = 1;
this.codedBlkLeft[0][blkY & 0x3] = 1;
}
}

View file

@ -0,0 +1,290 @@
package org.jcodec.codecs.h264.io;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.decode.CAVLCReader;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.common.SaveRestore;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.io.VLC;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.tools.MathUtil;
public class CAVLC implements SaveRestore {
private ColorSpace color;
private VLC chromaDCVLC;
private int[] tokensLeft;
private int[] tokensTop;
private int[] tokensLeftSaved;
private int[] tokensTopSaved;
private int mbWidth;
private int mbMask;
public CAVLC(SeqParameterSet sps, PictureParameterSet pps, int mbW, int mbH) {
this.color = sps.chromaFormatIdc;
this.chromaDCVLC = codeTableChromaDC();
this.mbWidth = sps.picWidthInMbsMinus1 + 1;
this.mbMask = (1 << mbH) - 1;
this.tokensLeft = new int[4];
this.tokensTop = new int[this.mbWidth << mbW];
this.tokensLeftSaved = new int[4];
this.tokensTopSaved = new int[this.mbWidth << mbW];
}
public void save() {
System.arraycopy(this.tokensLeft, 0, this.tokensLeftSaved, 0, this.tokensLeft.length);
System.arraycopy(this.tokensTop, 0, this.tokensTopSaved, 0, this.tokensTop.length);
}
public void restore() {
int[] tmp = this.tokensLeft;
this.tokensLeft = this.tokensLeftSaved;
this.tokensLeftSaved = tmp;
tmp = this.tokensTop;
this.tokensTop = this.tokensTopSaved;
this.tokensTopSaved = tmp;
}
public int writeACBlock(BitWriter out, int blkIndX, int blkIndY, MBType leftMBType, MBType topMBType, int[] coeff, VLC[] totalZerosTab, int firstCoeff, int maxCoeff, int[] scan) {
VLC coeffTokenTab = getCoeffTokenVLCForLuma((blkIndX != 0), leftMBType, this.tokensLeft[blkIndY & this.mbMask], (blkIndY != 0), topMBType, this.tokensTop[blkIndX]);
int coeffToken = writeBlockGen(out, coeff, totalZerosTab, firstCoeff, maxCoeff, scan, coeffTokenTab);
this.tokensLeft[blkIndY & this.mbMask] = coeffToken;
this.tokensTop[blkIndX] = coeffToken;
return coeffToken;
}
public void writeChrDCBlock(BitWriter out, int[] coeff, VLC[] totalZerosTab, int firstCoeff, int maxCoeff, int[] scan) {
writeBlockGen(out, coeff, totalZerosTab, firstCoeff, maxCoeff, scan, getCoeffTokenVLCForChromaDC());
}
public void writeLumaDCBlock(BitWriter out, int blkIndX, int blkIndY, MBType leftMBType, MBType topMBType, int[] coeff, VLC[] totalZerosTab, int firstCoeff, int maxCoeff, int[] scan) {
VLC coeffTokenTab = getCoeffTokenVLCForLuma((blkIndX != 0), leftMBType, this.tokensLeft[blkIndY & this.mbMask], (blkIndY != 0), topMBType, this.tokensTop[blkIndX]);
writeBlockGen(out, coeff, totalZerosTab, firstCoeff, maxCoeff, scan, coeffTokenTab);
}
private int writeBlockGen(BitWriter out, int[] coeff, VLC[] totalZerosTab, int firstCoeff, int maxCoeff, int[] scan, VLC coeffTokenTab) {
int trailingOnes = 0, totalCoeff = 0, totalZeros = 0;
int[] runBefore = new int[maxCoeff];
int[] levels = new int[maxCoeff];
for (int i = 0; i < maxCoeff; i++) {
int c = coeff[scan[i + firstCoeff]];
if (c == 0) {
runBefore[totalCoeff] = runBefore[totalCoeff] + 1;
totalZeros++;
} else {
levels[totalCoeff++] = c;
}
}
if (totalCoeff < maxCoeff)
totalZeros -= runBefore[totalCoeff];
trailingOnes = 0;
for (; trailingOnes < totalCoeff && trailingOnes < 3 && Math.abs(levels[totalCoeff - trailingOnes - 1]) == 1; trailingOnes++);
int coeffToken = H264Const.coeffToken(totalCoeff, trailingOnes);
coeffTokenTab.writeVLC(out, coeffToken);
if (totalCoeff > 0) {
writeTrailingOnes(out, levels, totalCoeff, trailingOnes);
writeLevels(out, levels, totalCoeff, trailingOnes);
if (totalCoeff < maxCoeff) {
totalZerosTab[totalCoeff - 1].writeVLC(out, totalZeros);
writeRuns(out, runBefore, totalCoeff, totalZeros);
}
}
return coeffToken;
}
private void writeTrailingOnes(BitWriter out, int[] levels, int totalCoeff, int trailingOne) {
for (int i = totalCoeff - 1; i >= totalCoeff - trailingOne; i--)
out.write1Bit(levels[i] >>> 31);
}
private void writeLevels(BitWriter out, int[] levels, int totalCoeff, int trailingOnes) {
int suffixLen = (totalCoeff > 10 && trailingOnes < 3) ? 1 : 0;
for (int i = totalCoeff - trailingOnes - 1; i >= 0; i--) {
int absLev = unsigned(levels[i]);
if (i == totalCoeff - trailingOnes - 1 && trailingOnes < 3)
absLev -= 2;
int prefix = absLev >> suffixLen;
if ((suffixLen == 0 && prefix < 14) || (suffixLen > 0 && prefix < 15)) {
out.writeNBit(1, prefix + 1);
out.writeNBit(absLev, suffixLen);
} else if (suffixLen == 0 && absLev < 30) {
out.writeNBit(1, 15);
out.writeNBit(absLev - 14, 4);
} else {
if (suffixLen == 0)
absLev -= 15;
int len, code;
for (len = 12; (code = absLev - (len + 3 << suffixLen) - (1 << len) + 4096) >= 1 << len; len++);
out.writeNBit(1, len + 4);
out.writeNBit(code, len);
}
if (suffixLen == 0)
suffixLen = 1;
if (MathUtil.abs(levels[i]) > 3 << suffixLen - 1 && suffixLen < 6)
suffixLen++;
}
}
private final int unsigned(int signed) {
int sign = signed >>> 31;
int s = signed >> 31;
return ((signed ^ s) - s << 1) + sign - 2;
}
private void writeRuns(BitWriter out, int[] run, int totalCoeff, int totalZeros) {
for (int i = totalCoeff - 1; i > 0 && totalZeros > 0; i--) {
H264Const.run[Math.min(6, totalZeros - 1)].writeVLC(out, run[i]);
totalZeros -= run[i];
}
}
public VLC getCoeffTokenVLCForLuma(boolean leftAvailable, MBType leftMBType, int leftToken, boolean topAvailable, MBType topMBType, int topToken) {
int nc = codeTableLuma(leftAvailable, leftMBType, leftToken, topAvailable, topMBType, topToken);
return H264Const.CoeffToken[Math.min(nc, 8)];
}
public VLC getCoeffTokenVLCForChromaDC() {
return this.chromaDCVLC;
}
protected int codeTableLuma(boolean leftAvailable, MBType leftMBType, int leftToken, boolean topAvailable, MBType topMBType, int topToken) {
int nA = (leftMBType == null) ? 0 : totalCoeff(leftToken);
int nB = (topMBType == null) ? 0 : totalCoeff(topToken);
if (leftAvailable && topAvailable)
return nA + nB + 1 >> 1;
if (leftAvailable)
return nA;
if (topAvailable)
return nB;
return 0;
}
protected VLC codeTableChromaDC() {
if (this.color == ColorSpace.YUV420J)
return H264Const.coeffTokenChromaDCY420;
if (this.color == ColorSpace.YUV422)
return H264Const.coeffTokenChromaDCY422;
if (this.color == ColorSpace.YUV444)
return H264Const.CoeffToken[0];
return null;
}
public int readCoeffs(BitReader _in, VLC coeffTokenTab, VLC[] totalZerosTab, int[] coeffLevel, int firstCoeff, int nCoeff, int[] zigzag) {
int coeffToken = coeffTokenTab.readVLC(_in);
int totalCoeff = totalCoeff(coeffToken);
int trailingOnes = trailingOnes(coeffToken);
if (totalCoeff > 0) {
int zerosLeft;
int suffixLength = (totalCoeff > 10 && trailingOnes < 3) ? 1 : 0;
int[] level = new int[totalCoeff];
int i;
for (i = 0; i < trailingOnes; i++)
level[i] = 1 - 2 * _in.read1Bit();
for (; i < totalCoeff; i++) {
int level_prefix = CAVLCReader.readZeroBitCount(_in, "");
int levelSuffixSize = suffixLength;
if (level_prefix == 14 && suffixLength == 0)
levelSuffixSize = 4;
if (level_prefix >= 15)
levelSuffixSize = level_prefix - 3;
int levelCode = Min(15, level_prefix) << suffixLength;
if (levelSuffixSize > 0) {
int level_suffix = CAVLCReader.readU(_in, levelSuffixSize, "RB: level_suffix");
levelCode += level_suffix;
}
if (level_prefix >= 15 && suffixLength == 0)
levelCode += 15;
if (level_prefix >= 16)
levelCode += (1 << level_prefix - 3) - 4096;
if (i == trailingOnes && trailingOnes < 3)
levelCode += 2;
if (levelCode % 2 == 0) {
level[i] = levelCode + 2 >> 1;
} else {
level[i] = -levelCode - 1 >> 1;
}
if (suffixLength == 0)
suffixLength = 1;
if (Abs(level[i]) > 3 << suffixLength - 1 && suffixLength < 6)
suffixLength++;
}
if (totalCoeff < nCoeff) {
if (coeffLevel.length == 4) {
zerosLeft = H264Const.totalZeros4[totalCoeff - 1].readVLC(_in);
} else if (coeffLevel.length == 8) {
zerosLeft = H264Const.totalZeros8[totalCoeff - 1].readVLC(_in);
} else {
zerosLeft = H264Const.totalZeros16[totalCoeff - 1].readVLC(_in);
}
} else {
zerosLeft = 0;
}
int[] runs = new int[totalCoeff];
int r;
for (r = 0; r < totalCoeff - 1 && zerosLeft > 0; r++) {
int run = H264Const.run[Math.min(6, zerosLeft - 1)].readVLC(_in);
zerosLeft -= run;
runs[r] = run;
}
runs[r] = zerosLeft;
for (int j = totalCoeff - 1, cn = 0; j >= 0 && cn < nCoeff; j--, cn++) {
cn += runs[j];
coeffLevel[zigzag[cn + firstCoeff]] = level[j];
}
}
return coeffToken;
}
private static int Min(int i, int level_prefix) {
return (i < level_prefix) ? i : level_prefix;
}
private static int Abs(int i) {
return (i < 0) ? -i : i;
}
public static final int totalCoeff(int coeffToken) {
return coeffToken >> 4;
}
public static final int trailingOnes(int coeffToken) {
return coeffToken & 0xF;
}
public static int[] NO_ZIGZAG = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15 };
public void readChromaDCBlock(BitReader reader, int[] coeff, boolean leftAvailable, boolean topAvailable) {
VLC coeffTokenTab = getCoeffTokenVLCForChromaDC();
readCoeffs(reader, coeffTokenTab, (coeff.length == 16) ? H264Const.totalZeros16 : (
(coeff.length == 8) ? H264Const.totalZeros8 : H264Const.totalZeros4), coeff, 0, coeff.length, NO_ZIGZAG);
}
public void readLumaDCBlock(BitReader reader, int[] coeff, int mbX, boolean leftAvailable, MBType leftMbType, boolean topAvailable, MBType topMbType, int[] zigzag4x4) {
VLC coeffTokenTab = getCoeffTokenVLCForLuma(leftAvailable, leftMbType, this.tokensLeft[0], topAvailable, topMbType, this.tokensTop[mbX << 2]);
readCoeffs(reader, coeffTokenTab, H264Const.totalZeros16, coeff, 0, 16, zigzag4x4);
}
public int readACBlock(BitReader reader, int[] coeff, int blkIndX, int blkIndY, boolean leftAvailable, MBType leftMbType, boolean topAvailable, MBType topMbType, int firstCoeff, int nCoeff, int[] zigzag4x4) {
VLC coeffTokenTab = getCoeffTokenVLCForLuma(leftAvailable, leftMbType, this.tokensLeft[blkIndY & this.mbMask], topAvailable, topMbType, this.tokensTop[blkIndX]);
int readCoeffs = readCoeffs(reader, coeffTokenTab, H264Const.totalZeros16, coeff, firstCoeff, nCoeff, zigzag4x4);
this.tokensTop[blkIndX] = readCoeffs;
this.tokensLeft[blkIndY & this.mbMask] = readCoeffs;
return totalCoeff(readCoeffs);
}
public void setZeroCoeff(int blkIndX, int blkIndY) {
this.tokensTop[blkIndX] = 0;
this.tokensLeft[blkIndY & this.mbMask] = 0;
}
}

View file

@ -0,0 +1,21 @@
package org.jcodec.codecs.h264.io.model;
public class AspectRatio {
public static final AspectRatio Extended_SAR = new AspectRatio(255);
private int value;
private AspectRatio(int value) {
this.value = value;
}
public static AspectRatio fromValue(int value) {
if (value == Extended_SAR.value)
return Extended_SAR;
return new AspectRatio(value);
}
public int getValue() {
return this.value;
}
}

View file

@ -0,0 +1,109 @@
package org.jcodec.codecs.h264.io.model;
import java.util.Comparator;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rect;
public class Frame extends Picture {
private int frameNo;
private SliceType frameType;
private H264Utils.MvList2D mvs;
private Frame[][][] refsUsed;
private boolean shortTerm;
private int poc;
public Frame(int width, int height, byte[][] data, ColorSpace color, Rect crop, int frameNo, SliceType frameType, H264Utils.MvList2D mvs, Frame[][][] refsUsed, int poc) {
super(width, height, data, null, color, 0, crop);
this.frameNo = frameNo;
this.mvs = mvs;
this.refsUsed = refsUsed;
this.poc = poc;
this.shortTerm = true;
}
public static Frame createFrame(Frame pic) {
Picture comp = pic.createCompatible();
return new Frame(comp.getWidth(), comp.getHeight(), comp.getData(), comp.getColor(), pic.getCrop(), pic.frameNo, pic.frameType, pic.mvs, pic.refsUsed, pic.poc);
}
public Frame cropped() {
Picture cropped = super.cropped();
return new Frame(cropped.getWidth(), cropped.getHeight(), cropped.getData(), cropped.getColor(), null, this.frameNo, this.frameType, this.mvs, this.refsUsed, this.poc);
}
public void copyFromFrame(Frame src) {
copyFrom(src);
this.frameNo = src.frameNo;
this.mvs = src.mvs;
this.shortTerm = src.shortTerm;
this.refsUsed = src.refsUsed;
this.poc = src.poc;
}
public Frame cloneCropped() {
if (cropNeeded())
return cropped();
Frame clone = createFrame(this);
clone.copyFrom(this);
return clone;
}
public int getFrameNo() {
return this.frameNo;
}
public H264Utils.MvList2D getMvs() {
return this.mvs;
}
public boolean isShortTerm() {
return this.shortTerm;
}
public void setShortTerm(boolean shortTerm) {
this.shortTerm = shortTerm;
}
public int getPOC() {
return this.poc;
}
public static Comparator<Frame> POCAsc = new Comparator<>() {
public int compare(Frame o1, Frame o2) {
if (o1 == null && o2 == null)
return 0;
if (o1 == null)
return 1;
if (o2 == null)
return -1;
return (o1.poc > o2.poc) ? 1 : ((o1.poc == o2.poc) ? 0 : -1);
}
};
public static Comparator<Frame> POCDesc = new Comparator<>() {
public int compare(Frame o1, Frame o2) {
if (o1 == null && o2 == null)
return 0;
if (o1 == null)
return 1;
if (o2 == null)
return -1;
return (o1.poc < o2.poc) ? 1 : ((o1.poc == o2.poc) ? 0 : -1);
}
};
public Frame[][][] getRefsUsed() {
return this.refsUsed;
}
public SliceType getFrameType() {
return this.frameType;
}
}

View file

@ -0,0 +1,23 @@
package org.jcodec.codecs.h264.io.model;
public class HRDParameters {
public int cpbCntMinus1;
public int bitRateScale;
public int cpbSizeScale;
public int[] bitRateValueMinus1;
public int[] cpbSizeValueMinus1;
public boolean[] cbrFlag;
public int initialCpbRemovalDelayLengthMinus1;
public int cpbRemovalDelayLengthMinus1;
public int dpbOutputDelayLengthMinus1;
public int timeOffsetLength;
}

View file

@ -0,0 +1,82 @@
package org.jcodec.codecs.h264.io.model;
public final class MBType {
public static final MBType I_NxN = new MBType(true, 0);
public static final MBType I_16x16 = new MBType(true, 1);
public static final MBType I_PCM = new MBType(true, 25);
public static final MBType P_16x16 = new MBType(false, 0);
public static final MBType P_16x8 = new MBType(false, 1);
public static final MBType P_8x16 = new MBType(false, 2);
public static final MBType P_8x8 = new MBType(false, 3);
public static final MBType P_8x8ref0 = new MBType(false, 4);
public static final MBType B_Direct_16x16 = new MBType(false, 0);
public static final MBType B_L0_16x16 = new MBType(false, 1);
public static final MBType B_L1_16x16 = new MBType(false, 2);
public static final MBType B_Bi_16x16 = new MBType(false, 3);
public static final MBType B_L0_L0_16x8 = new MBType(false, 4);
public static final MBType B_L0_L0_8x16 = new MBType(false, 5);
public static final MBType B_L1_L1_16x8 = new MBType(false, 6);
public static final MBType B_L1_L1_8x16 = new MBType(false, 7);
public static final MBType B_L0_L1_16x8 = new MBType(false, 8);
public static final MBType B_L0_L1_8x16 = new MBType(false, 9);
public static final MBType B_L1_L0_16x8 = new MBType(false, 10);
public static final MBType B_L1_L0_8x16 = new MBType(false, 11);
public static final MBType B_L0_Bi_16x8 = new MBType(false, 12);
public static final MBType B_L0_Bi_8x16 = new MBType(false, 13);
public static final MBType B_L1_Bi_16x8 = new MBType(false, 14);
public static final MBType B_L1_Bi_8x16 = new MBType(false, 15);
public static final MBType B_Bi_L0_16x8 = new MBType(false, 16);
public static final MBType B_Bi_L0_8x16 = new MBType(false, 17);
public static final MBType B_Bi_L1_16x8 = new MBType(false, 18);
public static final MBType B_Bi_L1_8x16 = new MBType(false, 19);
public static final MBType B_Bi_Bi_16x8 = new MBType(false, 20);
public static final MBType B_Bi_Bi_8x16 = new MBType(false, 21);
public static final MBType B_8x8 = new MBType(false, 22);
public boolean intra;
public int _code;
private MBType(boolean intra, int code) {
this.intra = intra;
this._code = code;
}
public boolean isIntra() {
return this.intra;
}
public int code() {
return this._code;
}
}

View file

@ -0,0 +1,27 @@
package org.jcodec.codecs.h264.io.model;
import java.nio.ByteBuffer;
public class NALUnit {
public NALUnitType type;
public int nal_ref_idc;
public NALUnit(NALUnitType type, int nal_ref_idc) {
this.type = type;
this.nal_ref_idc = nal_ref_idc;
}
public static NALUnit read(ByteBuffer _in) {
int nalu = _in.get() & 0xFF;
int nal_ref_idc = nalu >> 5 & 0x3;
int nb = nalu & 0x1F;
NALUnitType type = NALUnitType.fromValue(nb);
return new NALUnit(type, nal_ref_idc);
}
public void write(ByteBuffer out) {
int nalu = this.type.getValue() | this.nal_ref_idc << 5;
out.put((byte)nalu);
}
}

View file

@ -0,0 +1,72 @@
package org.jcodec.codecs.h264.io.model;
public final class NALUnitType {
public static final NALUnitType NON_IDR_SLICE = new NALUnitType(1, "NON_IDR_SLICE", "non IDR slice");
public static final NALUnitType SLICE_PART_A = new NALUnitType(2, "SLICE_PART_A", "slice part a");
public static final NALUnitType SLICE_PART_B = new NALUnitType(3, "SLICE_PART_B", "slice part b");
public static final NALUnitType SLICE_PART_C = new NALUnitType(4, "SLICE_PART_C", "slice part c");
public static final NALUnitType IDR_SLICE = new NALUnitType(5, "IDR_SLICE", "idr slice");
public static final NALUnitType SEI = new NALUnitType(6, "SEI", "sei");
public static final NALUnitType SPS = new NALUnitType(7, "SPS", "sequence parameter set");
public static final NALUnitType PPS = new NALUnitType(8, "PPS", "picture parameter set");
public static final NALUnitType ACC_UNIT_DELIM = new NALUnitType(9, "ACC_UNIT_DELIM", "access unit delimiter");
public static final NALUnitType END_OF_SEQ = new NALUnitType(10, "END_OF_SEQ", "end of sequence");
public static final NALUnitType END_OF_STREAM = new NALUnitType(11, "END_OF_STREAM", "end of stream");
public static final NALUnitType FILLER_DATA = new NALUnitType(12, "FILLER_DATA", "filler data");
public static final NALUnitType SEQ_PAR_SET_EXT = new NALUnitType(13, "SEQ_PAR_SET_EXT", "sequence parameter set extension");
public static final NALUnitType AUX_SLICE = new NALUnitType(19, "AUX_SLICE", "auxilary slice");
private static final NALUnitType[] _values = new NALUnitType[] {
NON_IDR_SLICE, SLICE_PART_A, SLICE_PART_B, SLICE_PART_C, IDR_SLICE, SEI, SPS, PPS, ACC_UNIT_DELIM, END_OF_SEQ,
END_OF_STREAM, FILLER_DATA, SEQ_PAR_SET_EXT, AUX_SLICE };
private static final NALUnitType[] lut = new NALUnitType[256];
private final int value;
private final String displayName;
private String _name;
static {
for (int i = 0; i < _values.length; i++) {
NALUnitType nalUnitType = _values[i];
lut[nalUnitType.value] = nalUnitType;
}
}
private NALUnitType(int value, String name, String displayName) {
this.value = value;
this._name = name;
this.displayName = displayName;
}
public String getName() {
return this.displayName;
}
public int getValue() {
return this.value;
}
public static NALUnitType fromValue(int value) {
return (value < lut.length) ? lut[value] : null;
}
public String toString() {
return this._name;
}
}

View file

@ -0,0 +1,400 @@
package org.jcodec.codecs.h264.io.model;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.jcodec.codecs.h264.decode.CAVLCReader;
import org.jcodec.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
import org.jcodec.platform.Platform;
public class PictureParameterSet {
public boolean entropyCodingModeFlag;
public int[] numRefIdxActiveMinus1;
public int sliceGroupChangeRateMinus1;
public int picParameterSetId;
public int seqParameterSetId;
public boolean picOrderPresentFlag;
public int numSliceGroupsMinus1;
public int sliceGroupMapType;
public boolean weightedPredFlag;
public int weightedBipredIdc;
public int picInitQpMinus26;
public int picInitQsMinus26;
public int chromaQpIndexOffset;
public boolean deblockingFilterControlPresentFlag;
public boolean constrainedIntraPredFlag;
public boolean redundantPicCntPresentFlag;
public int[] topLeft;
public int[] bottomRight;
public int[] runLengthMinus1;
public boolean sliceGroupChangeDirectionFlag;
public int[] sliceGroupId;
public PPSExt extended;
public static class PPSExt {
public boolean transform8x8ModeFlag;
public int[][] scalingMatrix;
public int secondChromaQpIndexOffset;
public boolean isTransform8x8ModeFlag() {
return this.transform8x8ModeFlag;
}
public int[][] getScalingMatrix() {
return this.scalingMatrix;
}
public int getSecondChromaQpIndexOffset() {
return this.secondChromaQpIndexOffset;
}
}
public PictureParameterSet() {
this.numRefIdxActiveMinus1 = new int[2];
}
public static PictureParameterSet read(ByteBuffer is) {
BitReader _in = BitReader.createBitReader(is);
PictureParameterSet pps = new PictureParameterSet();
pps.picParameterSetId = CAVLCReader.readUEtrace(_in, "PPS: pic_parameter_set_id");
pps.seqParameterSetId = CAVLCReader.readUEtrace(_in, "PPS: seq_parameter_set_id");
pps.entropyCodingModeFlag = CAVLCReader.readBool(_in, "PPS: entropy_coding_mode_flag");
pps.picOrderPresentFlag = CAVLCReader.readBool(_in, "PPS: pic_order_present_flag");
pps.numSliceGroupsMinus1 = CAVLCReader.readUEtrace(_in, "PPS: num_slice_groups_minus1");
if (pps.numSliceGroupsMinus1 > 0) {
pps.sliceGroupMapType = CAVLCReader.readUEtrace(_in, "PPS: slice_group_map_type");
pps.topLeft = new int[pps.numSliceGroupsMinus1 + 1];
pps.bottomRight = new int[pps.numSliceGroupsMinus1 + 1];
pps.runLengthMinus1 = new int[pps.numSliceGroupsMinus1 + 1];
if (pps.sliceGroupMapType == 0) {
for (int iGroup = 0; iGroup <= pps.numSliceGroupsMinus1; iGroup++)
pps.runLengthMinus1[iGroup] = CAVLCReader.readUEtrace(_in, "PPS: run_length_minus1");
} else if (pps.sliceGroupMapType == 2) {
for (int iGroup = 0; iGroup < pps.numSliceGroupsMinus1; iGroup++) {
pps.topLeft[iGroup] = CAVLCReader.readUEtrace(_in, "PPS: top_left");
pps.bottomRight[iGroup] = CAVLCReader.readUEtrace(_in, "PPS: bottom_right");
}
} else if (pps.sliceGroupMapType == 3 || pps.sliceGroupMapType == 4 || pps.sliceGroupMapType == 5) {
pps.sliceGroupChangeDirectionFlag = CAVLCReader.readBool(_in, "PPS: slice_group_change_direction_flag");
pps.sliceGroupChangeRateMinus1 = CAVLCReader.readUEtrace(_in, "PPS: slice_group_change_rate_minus1");
} else if (pps.sliceGroupMapType == 6) {
int NumberBitsPerSliceGroupId;
if (pps.numSliceGroupsMinus1 + 1 > 4) {
NumberBitsPerSliceGroupId = 3;
} else if (pps.numSliceGroupsMinus1 + 1 > 2) {
NumberBitsPerSliceGroupId = 2;
} else {
NumberBitsPerSliceGroupId = 1;
}
int pic_size_in_map_units_minus1 = CAVLCReader.readUEtrace(_in, "PPS: pic_size_in_map_units_minus1");
pps.sliceGroupId = new int[pic_size_in_map_units_minus1 + 1];
for (int i = 0; i <= pic_size_in_map_units_minus1; i++)
pps.sliceGroupId[i] = CAVLCReader.readU(_in, NumberBitsPerSliceGroupId, "PPS: slice_group_id [" + i + "]f");
}
}
pps.numRefIdxActiveMinus1 = new int[] { CAVLCReader.readUEtrace(_in, "PPS: num_ref_idx_l0_active_minus1"), CAVLCReader.readUEtrace(_in, "PPS: num_ref_idx_l1_active_minus1") };
pps.weightedPredFlag = CAVLCReader.readBool(_in, "PPS: weighted_pred_flag");
pps.weightedBipredIdc = CAVLCReader.readNBit(_in, 2, "PPS: weighted_bipred_idc");
pps.picInitQpMinus26 = CAVLCReader.readSE(_in, "PPS: pic_init_qp_minus26");
pps.picInitQsMinus26 = CAVLCReader.readSE(_in, "PPS: pic_init_qs_minus26");
pps.chromaQpIndexOffset = CAVLCReader.readSE(_in, "PPS: chroma_qp_index_offset");
pps.deblockingFilterControlPresentFlag = CAVLCReader.readBool(_in, "PPS: deblocking_filter_control_present_flag");
pps.constrainedIntraPredFlag = CAVLCReader.readBool(_in, "PPS: constrained_intra_pred_flag");
pps.redundantPicCntPresentFlag = CAVLCReader.readBool(_in, "PPS: redundant_pic_cnt_present_flag");
if (CAVLCReader.moreRBSPData(_in)) {
pps.extended = new PPSExt();
pps.extended.transform8x8ModeFlag = CAVLCReader.readBool(_in, "PPS: transform_8x8_mode_flag");
boolean pic_scaling_matrix_present_flag = CAVLCReader.readBool(_in, "PPS: pic_scaling_matrix_present_flag");
if (pic_scaling_matrix_present_flag) {
pps.extended.scalingMatrix = new int[8][];
int i = 0;
while (true) {
if (i < 6 + 2 * (pps.extended.transform8x8ModeFlag ? 1 : 0)) {
int scalingListSize = (i < 6) ? 16 : 64;
if (CAVLCReader.readBool(_in, "PPS: pic_scaling_list_present_flag"))
pps.extended.scalingMatrix[i] = SeqParameterSet.readScalingList(_in, scalingListSize);
i++;
continue;
}
break;
}
}
pps.extended.secondChromaQpIndexOffset = CAVLCReader.readSE(_in, "PPS: second_chroma_qp_index_offset");
}
return pps;
}
public void write(ByteBuffer out) {
BitWriter writer = new BitWriter(out);
CAVLCWriter.writeUEtrace(writer, this.picParameterSetId, "PPS: pic_parameter_set_id");
CAVLCWriter.writeUEtrace(writer, this.seqParameterSetId, "PPS: seq_parameter_set_id");
CAVLCWriter.writeBool(writer, this.entropyCodingModeFlag, "PPS: entropy_coding_mode_flag");
CAVLCWriter.writeBool(writer, this.picOrderPresentFlag, "PPS: pic_order_present_flag");
CAVLCWriter.writeUEtrace(writer, this.numSliceGroupsMinus1, "PPS: num_slice_groups_minus1");
if (this.numSliceGroupsMinus1 > 0) {
CAVLCWriter.writeUEtrace(writer, this.sliceGroupMapType, "PPS: slice_group_map_type");
if (this.sliceGroupMapType == 0) {
for (int iGroup = 0; iGroup <= this.numSliceGroupsMinus1; iGroup++)
CAVLCWriter.writeUEtrace(writer, this.runLengthMinus1[iGroup], "PPS: ");
} else if (this.sliceGroupMapType == 2) {
for (int iGroup = 0; iGroup < this.numSliceGroupsMinus1; iGroup++) {
CAVLCWriter.writeUEtrace(writer, this.topLeft[iGroup], "PPS: ");
CAVLCWriter.writeUEtrace(writer, this.bottomRight[iGroup], "PPS: ");
}
} else if (this.sliceGroupMapType == 3 || this.sliceGroupMapType == 4 || this.sliceGroupMapType == 5) {
CAVLCWriter.writeBool(writer, this.sliceGroupChangeDirectionFlag, "PPS: slice_group_change_direction_flag");
CAVLCWriter.writeUEtrace(writer, this.sliceGroupChangeRateMinus1, "PPS: slice_group_change_rate_minus1");
} else if (this.sliceGroupMapType == 6) {
int NumberBitsPerSliceGroupId;
if (this.numSliceGroupsMinus1 + 1 > 4) {
NumberBitsPerSliceGroupId = 3;
} else if (this.numSliceGroupsMinus1 + 1 > 2) {
NumberBitsPerSliceGroupId = 2;
} else {
NumberBitsPerSliceGroupId = 1;
}
CAVLCWriter.writeUEtrace(writer, this.sliceGroupId.length, "PPS: ");
for (int i = 0; i <= this.sliceGroupId.length; i++)
CAVLCWriter.writeU(writer, this.sliceGroupId[i], NumberBitsPerSliceGroupId);
}
}
CAVLCWriter.writeUEtrace(writer, this.numRefIdxActiveMinus1[0], "PPS: num_ref_idx_l0_active_minus1");
CAVLCWriter.writeUEtrace(writer, this.numRefIdxActiveMinus1[1], "PPS: num_ref_idx_l1_active_minus1");
CAVLCWriter.writeBool(writer, this.weightedPredFlag, "PPS: weighted_pred_flag");
CAVLCWriter.writeNBit(writer, (long)this.weightedBipredIdc, 2, "PPS: weighted_bipred_idc");
CAVLCWriter.writeSEtrace(writer, this.picInitQpMinus26, "PPS: pic_init_qp_minus26");
CAVLCWriter.writeSEtrace(writer, this.picInitQsMinus26, "PPS: pic_init_qs_minus26");
CAVLCWriter.writeSEtrace(writer, this.chromaQpIndexOffset, "PPS: chroma_qp_index_offset");
CAVLCWriter.writeBool(writer, this.deblockingFilterControlPresentFlag, "PPS: deblocking_filter_control_present_flag");
CAVLCWriter.writeBool(writer, this.constrainedIntraPredFlag, "PPS: constrained_intra_pred_flag");
CAVLCWriter.writeBool(writer, this.redundantPicCntPresentFlag, "PPS: redundant_pic_cnt_present_flag");
if (this.extended != null) {
CAVLCWriter.writeBool(writer, this.extended.transform8x8ModeFlag, "PPS: transform_8x8_mode_flag");
CAVLCWriter.writeBool(writer, (this.extended.scalingMatrix != null), "PPS: scalindMatrix");
if (this.extended.scalingMatrix != null) {
int i = 0;
while (true) {
if (i < 6 + 2 * (this.extended.transform8x8ModeFlag ? 1 : 0)) {
CAVLCWriter.writeBool(writer, (this.extended.scalingMatrix[i] != null), "PPS: ");
if (this.extended.scalingMatrix[i] != null)
SeqParameterSet.writeScalingList(writer, this.extended.scalingMatrix, i);
i++;
continue;
}
break;
}
}
CAVLCWriter.writeSEtrace(writer, this.extended.secondChromaQpIndexOffset, "PPS: ");
}
CAVLCWriter.writeTrailingBits(writer);
}
public int hashCode() {
int prime = 31;
int result = 1;
result = 31 * result + Arrays.hashCode(this.bottomRight);
result = 31 * result + this.chromaQpIndexOffset;
result = 31 * result + (this.constrainedIntraPredFlag ? 1231 : 1237);
result = 31 * result + (this.deblockingFilterControlPresentFlag ? 1231 : 1237);
result = 31 * result + (this.entropyCodingModeFlag ? 1231 : 1237);
result = 31 * result + ((this.extended == null) ? 0 : this.extended.hashCode());
result = 31 * result + this.numRefIdxActiveMinus1[0];
result = 31 * result + this.numRefIdxActiveMinus1[1];
result = 31 * result + this.numSliceGroupsMinus1;
result = 31 * result + this.picInitQpMinus26;
result = 31 * result + this.picInitQsMinus26;
result = 31 * result + (this.picOrderPresentFlag ? 1231 : 1237);
result = 31 * result + this.picParameterSetId;
result = 31 * result + (this.redundantPicCntPresentFlag ? 1231 : 1237);
result = 31 * result + Arrays.hashCode(this.runLengthMinus1);
result = 31 * result + this.seqParameterSetId;
result = 31 * result + (this.sliceGroupChangeDirectionFlag ? 1231 : 1237);
result = 31 * result + this.sliceGroupChangeRateMinus1;
result = 31 * result + Arrays.hashCode(this.sliceGroupId);
result = 31 * result + this.sliceGroupMapType;
result = 31 * result + Arrays.hashCode(this.topLeft);
result = 31 * result + this.weightedBipredIdc;
result = 31 * result + (this.weightedPredFlag ? 1231 : 1237);
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PictureParameterSet other = (PictureParameterSet)obj;
if (!Platform.arrayEqualsInt(this.bottomRight, other.bottomRight))
return false;
if (this.chromaQpIndexOffset != other.chromaQpIndexOffset)
return false;
if (this.constrainedIntraPredFlag != other.constrainedIntraPredFlag)
return false;
if (this.deblockingFilterControlPresentFlag != other.deblockingFilterControlPresentFlag)
return false;
if (this.entropyCodingModeFlag != other.entropyCodingModeFlag)
return false;
if (this.extended == null) {
if (other.extended != null)
return false;
} else if (!this.extended.equals(other.extended)) {
return false;
}
if (this.numRefIdxActiveMinus1[0] != other.numRefIdxActiveMinus1[0])
return false;
if (this.numRefIdxActiveMinus1[1] != other.numRefIdxActiveMinus1[1])
return false;
if (this.numSliceGroupsMinus1 != other.numSliceGroupsMinus1)
return false;
if (this.picInitQpMinus26 != other.picInitQpMinus26)
return false;
if (this.picInitQsMinus26 != other.picInitQsMinus26)
return false;
if (this.picOrderPresentFlag != other.picOrderPresentFlag)
return false;
if (this.picParameterSetId != other.picParameterSetId)
return false;
if (this.redundantPicCntPresentFlag != other.redundantPicCntPresentFlag)
return false;
if (!Platform.arrayEqualsInt(this.runLengthMinus1, other.runLengthMinus1))
return false;
if (this.seqParameterSetId != other.seqParameterSetId)
return false;
if (this.sliceGroupChangeDirectionFlag != other.sliceGroupChangeDirectionFlag)
return false;
if (this.sliceGroupChangeRateMinus1 != other.sliceGroupChangeRateMinus1)
return false;
if (!Platform.arrayEqualsInt(this.sliceGroupId, other.sliceGroupId))
return false;
if (this.sliceGroupMapType != other.sliceGroupMapType)
return false;
if (!Platform.arrayEqualsInt(this.topLeft, other.topLeft))
return false;
if (this.weightedBipredIdc != other.weightedBipredIdc)
return false;
if (this.weightedPredFlag != other.weightedPredFlag)
return false;
return true;
}
public PictureParameterSet copy() {
ByteBuffer buf = ByteBuffer.allocate(2048);
write(buf);
buf.flip();
return read(buf);
}
public boolean isEntropyCodingModeFlag() {
return this.entropyCodingModeFlag;
}
public int[] getNumRefIdxActiveMinus1() {
return this.numRefIdxActiveMinus1;
}
public int getSliceGroupChangeRateMinus1() {
return this.sliceGroupChangeRateMinus1;
}
public int getPicParameterSetId() {
return this.picParameterSetId;
}
public int getSeqParameterSetId() {
return this.seqParameterSetId;
}
public boolean isPicOrderPresentFlag() {
return this.picOrderPresentFlag;
}
public int getNumSliceGroupsMinus1() {
return this.numSliceGroupsMinus1;
}
public int getSliceGroupMapType() {
return this.sliceGroupMapType;
}
public boolean isWeightedPredFlag() {
return this.weightedPredFlag;
}
public int getWeightedBipredIdc() {
return this.weightedBipredIdc;
}
public int getPicInitQpMinus26() {
return this.picInitQpMinus26;
}
public int getPicInitQsMinus26() {
return this.picInitQsMinus26;
}
public int getChromaQpIndexOffset() {
return this.chromaQpIndexOffset;
}
public boolean isDeblockingFilterControlPresentFlag() {
return this.deblockingFilterControlPresentFlag;
}
public boolean isConstrainedIntraPredFlag() {
return this.constrainedIntraPredFlag;
}
public boolean isRedundantPicCntPresentFlag() {
return this.redundantPicCntPresentFlag;
}
public int[] getTopLeft() {
return this.topLeft;
}
public int[] getBottomRight() {
return this.bottomRight;
}
public int[] getRunLengthMinus1() {
return this.runLengthMinus1;
}
public boolean isSliceGroupChangeDirectionFlag() {
return this.sliceGroupChangeDirectionFlag;
}
public int[] getSliceGroupId() {
return this.sliceGroupId;
}
public PPSExt getExtended() {
return this.extended;
}
}

View file

@ -0,0 +1,15 @@
package org.jcodec.codecs.h264.io.model;
public class PredictionWeightTable {
public int lumaLog2WeightDenom;
public int chromaLog2WeightDenom;
public int[][] lumaWeight = new int[2][];
public int[][][] chromaWeight = new int[2][][];
public int[][] lumaOffset = new int[2][];
public int[][][] chromaOffset = new int[2][][];
}

View file

@ -0,0 +1,49 @@
package org.jcodec.codecs.h264.io.model;
import org.jcodec.platform.Platform;
public class RefPicMarking {
private Instruction[] instructions;
public enum InstrType {
REMOVE_SHORT, REMOVE_LONG, CONVERT_INTO_LONG, TRUNK_LONG, CLEAR, MARK_LONG;
}
public static class Instruction {
private RefPicMarking.InstrType type;
private int arg1;
private int arg2;
public Instruction(RefPicMarking.InstrType type, int arg1, int arg2) {
this.type = type;
this.arg1 = arg1;
this.arg2 = arg2;
}
public RefPicMarking.InstrType getType() {
return this.type;
}
public int getArg1() {
return this.arg1;
}
public int getArg2() {
return this.arg2;
}
}
public RefPicMarking(Instruction[] instructions) {
this.instructions = instructions;
}
public Instruction[] getInstructions() {
return this.instructions;
}
public String toString() {
return Platform.toJSON(this);
}
}

View file

@ -0,0 +1,26 @@
package org.jcodec.codecs.h264.io.model;
import org.jcodec.platform.Platform;
public class RefPicMarkingIDR {
boolean discardDecodedPics;
boolean useForlongTerm;
public RefPicMarkingIDR(boolean discardDecodedPics, boolean useForlongTerm) {
this.discardDecodedPics = discardDecodedPics;
this.useForlongTerm = useForlongTerm;
}
public boolean isDiscardDecodedPics() {
return this.discardDecodedPics;
}
public boolean isUseForlongTerm() {
return this.useForlongTerm;
}
public String toString() {
return Platform.toJSON(this);
}
}

View file

@ -0,0 +1,72 @@
package org.jcodec.codecs.h264.io.model;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.common.io.BitWriter;
public class SEI {
public SEIMessage[] messages;
public static class SEIMessage {
public int payloadType;
public int payloadSize;
public byte[] payload;
public SEIMessage(int payloadType2, int payloadSize2, byte[] payload2) {
this.payload = payload2;
this.payloadType = payloadType2;
this.payloadSize = payloadSize2;
}
}
public SEI(SEIMessage[] messages) {
this.messages = messages;
}
public static SEI read(ByteBuffer is) {
SEIMessage msg;
List<SEIMessage> messages = new ArrayList<>();
do {
msg = sei_message(is);
if (msg == null)
continue;
messages.add(msg);
} while (msg != null);
return new SEI(messages.<SEIMessage>toArray(new SEIMessage[0]));
}
private static SEIMessage sei_message(ByteBuffer is) {
int payloadType = 0;
int b = 0;
while (is.hasRemaining() && (b = is.get() & 0xFF) == 255)
payloadType += 255;
if (!is.hasRemaining())
return null;
payloadType += b;
int payloadSize = 0;
while (is.hasRemaining() && (b = is.get() & 0xFF) == 255)
payloadSize += 255;
if (!is.hasRemaining())
return null;
payloadSize += b;
byte[] payload = sei_payload(payloadType, payloadSize, is);
if (payload.length != payloadSize)
return null;
return new SEIMessage(payloadType, payloadSize, payload);
}
private static byte[] sei_payload(int payloadType, int payloadSize, ByteBuffer is) {
byte[] res = new byte[payloadSize];
is.get(res);
return res;
}
public void write(ByteBuffer out) {
BitWriter writer = new BitWriter(out);
CAVLCWriter.writeTrailingBits(writer);
}
}

View file

@ -0,0 +1,608 @@
package org.jcodec.codecs.h264.io.model;
import java.nio.ByteBuffer;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.decode.CAVLCReader;
import org.jcodec.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.platform.Platform;
public class SeqParameterSet {
public int picOrderCntType;
public boolean fieldPicFlag;
public boolean deltaPicOrderAlwaysZeroFlag;
public boolean mbAdaptiveFrameFieldFlag;
public boolean direct8x8InferenceFlag;
public ColorSpace chromaFormatIdc;
public int log2MaxFrameNumMinus4;
public int log2MaxPicOrderCntLsbMinus4;
public int picHeightInMapUnitsMinus1;
public int picWidthInMbsMinus1;
public int bitDepthLumaMinus8;
public int bitDepthChromaMinus8;
public boolean qpprimeYZeroTransformBypassFlag;
public int profileIdc;
public boolean constraintSet0Flag;
public boolean constraintSet1Flag;
public boolean constraintSet2Flag;
public boolean constraintSet3Flag;
public boolean constraintSet4Flag;
public boolean constraintSet5Flag;
public int levelIdc;
public int seqParameterSetId;
public boolean separateColourPlaneFlag;
public int offsetForNonRefPic;
public int offsetForTopToBottomField;
public int numRefFrames;
public boolean gapsInFrameNumValueAllowedFlag;
public boolean frameMbsOnlyFlag;
public boolean frameCroppingFlag;
public int frameCropLeftOffset;
public int frameCropRightOffset;
public int frameCropTopOffset;
public int frameCropBottomOffset;
public int[] offsetForRefFrame;
public VUIParameters vuiParams;
public int[][] scalingMatrix;
public int numRefFramesInPicOrderCntCycle;
public static ColorSpace getColor(int id) {
switch (id) {
case 0:
return ColorSpace.MONO;
case 1:
return ColorSpace.YUV420J;
case 2:
return ColorSpace.YUV422;
case 3:
return ColorSpace.YUV444;
}
throw new RuntimeException("Colorspace not supported");
}
public static int fromColor(ColorSpace color) {
if (color == ColorSpace.MONO)
return 0;
if (color == ColorSpace.YUV420J)
return 1;
if (color == ColorSpace.YUV422)
return 2;
if (color == ColorSpace.YUV444)
return 3;
throw new RuntimeException("Colorspace not supported");
}
public static SeqParameterSet read(ByteBuffer is) {
BitReader _in = BitReader.createBitReader(is);
SeqParameterSet sps = new SeqParameterSet();
sps.profileIdc = CAVLCReader.readNBit(_in, 8, "SPS: profile_idc");
sps.constraintSet0Flag = CAVLCReader.readBool(_in, "SPS: constraint_set_0_flag");
sps.constraintSet1Flag = CAVLCReader.readBool(_in, "SPS: constraint_set_1_flag");
sps.constraintSet2Flag = CAVLCReader.readBool(_in, "SPS: constraint_set_2_flag");
sps.constraintSet3Flag = CAVLCReader.readBool(_in, "SPS: constraint_set_3_flag");
sps.constraintSet4Flag = CAVLCReader.readBool(_in, "SPS: constraint_set_4_flag");
sps.constraintSet5Flag = CAVLCReader.readBool(_in, "SPS: constraint_set_5_flag");
CAVLCReader.readNBit(_in, 2, "SPS: reserved_zero_2bits");
sps.levelIdc = CAVLCReader.readNBit(_in, 8, "SPS: level_idc");
sps.seqParameterSetId = CAVLCReader.readUEtrace(_in, "SPS: seq_parameter_set_id");
if (sps.profileIdc == 100 || sps.profileIdc == 110 || sps.profileIdc == 122 || sps.profileIdc == 144) {
sps.chromaFormatIdc = getColor(CAVLCReader.readUEtrace(_in, "SPS: chroma_format_idc"));
if (sps.chromaFormatIdc == ColorSpace.YUV444)
sps.separateColourPlaneFlag = CAVLCReader.readBool(_in, "SPS: separate_colour_plane_flag");
sps.bitDepthLumaMinus8 = CAVLCReader.readUEtrace(_in, "SPS: bit_depth_luma_minus8");
sps.bitDepthChromaMinus8 = CAVLCReader.readUEtrace(_in, "SPS: bit_depth_chroma_minus8");
sps.qpprimeYZeroTransformBypassFlag = CAVLCReader.readBool(_in, "SPS: qpprime_y_zero_transform_bypass_flag");
boolean seqScalingMatrixPresent = CAVLCReader.readBool(_in, "SPS: seq_scaling_matrix_present_lag");
if (seqScalingMatrixPresent)
readScalingListMatrix(_in, sps);
} else {
sps.chromaFormatIdc = ColorSpace.YUV420J;
}
sps.log2MaxFrameNumMinus4 = CAVLCReader.readUEtrace(_in, "SPS: log2_max_frame_num_minus4");
sps.picOrderCntType = CAVLCReader.readUEtrace(_in, "SPS: pic_order_cnt_type");
if (sps.picOrderCntType == 0) {
sps.log2MaxPicOrderCntLsbMinus4 = CAVLCReader.readUEtrace(_in, "SPS: log2_max_pic_order_cnt_lsb_minus4");
} else if (sps.picOrderCntType == 1) {
sps.deltaPicOrderAlwaysZeroFlag = CAVLCReader.readBool(_in, "SPS: delta_pic_order_always_zero_flag");
sps.offsetForNonRefPic = CAVLCReader.readSE(_in, "SPS: offset_for_non_ref_pic");
sps.offsetForTopToBottomField = CAVLCReader.readSE(_in, "SPS: offset_for_top_to_bottom_field");
sps.numRefFramesInPicOrderCntCycle = CAVLCReader.readUEtrace(_in, "SPS: num_ref_frames_in_pic_order_cnt_cycle");
sps.offsetForRefFrame = new int[sps.numRefFramesInPicOrderCntCycle];
for (int i = 0; i < sps.numRefFramesInPicOrderCntCycle; i++)
sps.offsetForRefFrame[i] = CAVLCReader.readSE(_in, "SPS: offsetForRefFrame [" + i + "]");
}
sps.numRefFrames = CAVLCReader.readUEtrace(_in, "SPS: num_ref_frames");
sps.gapsInFrameNumValueAllowedFlag = CAVLCReader.readBool(_in, "SPS: gaps_in_frame_num_value_allowed_flag");
sps.picWidthInMbsMinus1 = CAVLCReader.readUEtrace(_in, "SPS: pic_width_in_mbs_minus1");
sps.picHeightInMapUnitsMinus1 = CAVLCReader.readUEtrace(_in, "SPS: pic_height_in_map_units_minus1");
sps.frameMbsOnlyFlag = CAVLCReader.readBool(_in, "SPS: frame_mbs_only_flag");
if (!sps.frameMbsOnlyFlag)
sps.mbAdaptiveFrameFieldFlag = CAVLCReader.readBool(_in, "SPS: mb_adaptive_frame_field_flag");
sps.direct8x8InferenceFlag = CAVLCReader.readBool(_in, "SPS: direct_8x8_inference_flag");
sps.frameCroppingFlag = CAVLCReader.readBool(_in, "SPS: frame_cropping_flag");
if (sps.frameCroppingFlag) {
sps.frameCropLeftOffset = CAVLCReader.readUEtrace(_in, "SPS: frame_crop_left_offset");
sps.frameCropRightOffset = CAVLCReader.readUEtrace(_in, "SPS: frame_crop_right_offset");
sps.frameCropTopOffset = CAVLCReader.readUEtrace(_in, "SPS: frame_crop_top_offset");
sps.frameCropBottomOffset = CAVLCReader.readUEtrace(_in, "SPS: frame_crop_bottom_offset");
}
boolean vuiParametersPresentFlag = CAVLCReader.readBool(_in, "SPS: vui_parameters_present_flag");
if (vuiParametersPresentFlag)
sps.vuiParams = readVUIParameters(_in);
return sps;
}
public static void writeScalingList(BitWriter out, int[][] scalingMatrix, int which) {
boolean useDefaultScalingMatrixFlag = false;
switch (which) {
case 0:
useDefaultScalingMatrixFlag = Platform.arrayEqualsInt(scalingMatrix[which], H264Const.defaultScalingList4x4Intra);
break;
case 1:
case 2:
useDefaultScalingMatrixFlag = Platform.arrayEqualsInt(scalingMatrix[which], scalingMatrix[0]);
break;
case 3:
useDefaultScalingMatrixFlag = Platform.arrayEqualsInt(scalingMatrix[which], H264Const.defaultScalingList4x4Inter);
break;
case 4:
case 5:
useDefaultScalingMatrixFlag = Platform.arrayEqualsInt(scalingMatrix[which], scalingMatrix[3]);
break;
case 6:
useDefaultScalingMatrixFlag = Platform.arrayEqualsInt(scalingMatrix[which], H264Const.defaultScalingList8x8Intra);
break;
case 7:
useDefaultScalingMatrixFlag = Platform.arrayEqualsInt(scalingMatrix[which], H264Const.defaultScalingList8x8Inter);
break;
}
int[] scalingList = scalingMatrix[which];
if (useDefaultScalingMatrixFlag) {
CAVLCWriter.writeSEtrace(out, -8, "SPS: ");
return;
}
int lastScale = 8;
int nextScale = 8;
for (int j = 0; j < scalingList.length; j++) {
if (nextScale != 0) {
int deltaScale = scalingList[j] - lastScale - 256;
CAVLCWriter.writeSEtrace(out, deltaScale, "SPS: ");
}
lastScale = scalingList[j];
}
}
public static int[] readScalingList(BitReader src, int sizeOfScalingList) {
int[] scalingList = new int[sizeOfScalingList];
int lastScale = 8;
int nextScale = 8;
for (int j = 0; j < sizeOfScalingList; j++) {
if (nextScale != 0) {
int deltaScale = CAVLCReader.readSE(src, "deltaScale");
nextScale = (lastScale + deltaScale + 256) % 256;
if (j == 0 && nextScale == 0)
return null;
}
scalingList[j] = (nextScale == 0) ? lastScale : nextScale;
lastScale = scalingList[j];
}
return scalingList;
}
private static void readScalingListMatrix(BitReader src, SeqParameterSet sps) {
sps.scalingMatrix = new int[8][];
for (int i = 0; i < 8; i++) {
boolean seqScalingListPresentFlag = CAVLCReader.readBool(src, "SPS: seqScalingListPresentFlag");
if (seqScalingListPresentFlag) {
int scalingListSize = (i < 6) ? 16 : 64;
sps.scalingMatrix[i] = readScalingList(src, scalingListSize);
}
}
}
private static VUIParameters readVUIParameters(BitReader _in) {
VUIParameters vuip = new VUIParameters();
vuip.aspectRatioInfoPresentFlag = CAVLCReader.readBool(_in, "VUI: aspect_ratio_info_present_flag");
if (vuip.aspectRatioInfoPresentFlag) {
vuip.aspectRatio = AspectRatio.fromValue(CAVLCReader.readNBit(_in, 8, "VUI: aspect_ratio"));
if (vuip.aspectRatio == AspectRatio.Extended_SAR) {
vuip.sarWidth = CAVLCReader.readNBit(_in, 16, "VUI: sar_width");
vuip.sarHeight = CAVLCReader.readNBit(_in, 16, "VUI: sar_height");
}
}
vuip.overscanInfoPresentFlag = CAVLCReader.readBool(_in, "VUI: overscan_info_present_flag");
if (vuip.overscanInfoPresentFlag)
vuip.overscanAppropriateFlag = CAVLCReader.readBool(_in, "VUI: overscan_appropriate_flag");
vuip.videoSignalTypePresentFlag = CAVLCReader.readBool(_in, "VUI: video_signal_type_present_flag");
if (vuip.videoSignalTypePresentFlag) {
vuip.videoFormat = CAVLCReader.readNBit(_in, 3, "VUI: video_format");
vuip.videoFullRangeFlag = CAVLCReader.readBool(_in, "VUI: video_full_range_flag");
vuip.colourDescriptionPresentFlag = CAVLCReader.readBool(_in, "VUI: colour_description_present_flag");
if (vuip.colourDescriptionPresentFlag) {
vuip.colourPrimaries = CAVLCReader.readNBit(_in, 8, "VUI: colour_primaries");
vuip.transferCharacteristics = CAVLCReader.readNBit(_in, 8, "VUI: transfer_characteristics");
vuip.matrixCoefficients = CAVLCReader.readNBit(_in, 8, "VUI: matrix_coefficients");
}
}
vuip.chromaLocInfoPresentFlag = CAVLCReader.readBool(_in, "VUI: chroma_loc_info_present_flag");
if (vuip.chromaLocInfoPresentFlag) {
vuip.chromaSampleLocTypeTopField = CAVLCReader.readUEtrace(_in, "VUI chroma_sample_loc_type_top_field");
vuip.chromaSampleLocTypeBottomField = CAVLCReader.readUEtrace(_in, "VUI chroma_sample_loc_type_bottom_field");
}
vuip.timingInfoPresentFlag = CAVLCReader.readBool(_in, "VUI: timing_info_present_flag");
if (vuip.timingInfoPresentFlag) {
vuip.numUnitsInTick = CAVLCReader.readNBit(_in, 32, "VUI: num_units_in_tick");
vuip.timeScale = CAVLCReader.readNBit(_in, 32, "VUI: time_scale");
vuip.fixedFrameRateFlag = CAVLCReader.readBool(_in, "VUI: fixed_frame_rate_flag");
}
boolean nalHRDParametersPresentFlag = CAVLCReader.readBool(_in, "VUI: nal_hrd_parameters_present_flag");
if (nalHRDParametersPresentFlag)
vuip.nalHRDParams = readHRDParameters(_in);
boolean vclHRDParametersPresentFlag = CAVLCReader.readBool(_in, "VUI: vcl_hrd_parameters_present_flag");
if (vclHRDParametersPresentFlag)
vuip.vclHRDParams = readHRDParameters(_in);
if (nalHRDParametersPresentFlag || vclHRDParametersPresentFlag)
vuip.lowDelayHrdFlag = CAVLCReader.readBool(_in, "VUI: low_delay_hrd_flag");
vuip.picStructPresentFlag = CAVLCReader.readBool(_in, "VUI: pic_struct_present_flag");
boolean bitstreamRestrictionFlag = CAVLCReader.readBool(_in, "VUI: bitstream_restriction_flag");
if (bitstreamRestrictionFlag) {
vuip.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
vuip.bitstreamRestriction.motionVectorsOverPicBoundariesFlag = CAVLCReader.readBool(_in, "VUI: motion_vectors_over_pic_boundaries_flag");
vuip.bitstreamRestriction.maxBytesPerPicDenom = CAVLCReader.readUEtrace(_in, "VUI max_bytes_per_pic_denom");
vuip.bitstreamRestriction.maxBitsPerMbDenom = CAVLCReader.readUEtrace(_in, "VUI max_bits_per_mb_denom");
vuip.bitstreamRestriction.log2MaxMvLengthHorizontal = CAVLCReader.readUEtrace(_in, "VUI log2_max_mv_length_horizontal");
vuip.bitstreamRestriction.log2MaxMvLengthVertical = CAVLCReader.readUEtrace(_in, "VUI log2_max_mv_length_vertical");
vuip.bitstreamRestriction.numReorderFrames = CAVLCReader.readUEtrace(_in, "VUI num_reorder_frames");
vuip.bitstreamRestriction.maxDecFrameBuffering = CAVLCReader.readUEtrace(_in, "VUI max_dec_frame_buffering");
}
return vuip;
}
private static HRDParameters readHRDParameters(BitReader _in) {
HRDParameters hrd = new HRDParameters();
hrd.cpbCntMinus1 = CAVLCReader.readUEtrace(_in, "SPS: cpb_cnt_minus1");
hrd.bitRateScale = CAVLCReader.readNBit(_in, 4, "HRD: bit_rate_scale");
hrd.cpbSizeScale = CAVLCReader.readNBit(_in, 4, "HRD: cpb_size_scale");
hrd.bitRateValueMinus1 = new int[hrd.cpbCntMinus1 + 1];
hrd.cpbSizeValueMinus1 = new int[hrd.cpbCntMinus1 + 1];
hrd.cbrFlag = new boolean[hrd.cpbCntMinus1 + 1];
for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpbCntMinus1; SchedSelIdx++) {
hrd.bitRateValueMinus1[SchedSelIdx] = CAVLCReader.readUEtrace(_in, "HRD: bit_rate_value_minus1");
hrd.cpbSizeValueMinus1[SchedSelIdx] = CAVLCReader.readUEtrace(_in, "HRD: cpb_size_value_minus1");
hrd.cbrFlag[SchedSelIdx] = CAVLCReader.readBool(_in, "HRD: cbr_flag");
}
hrd.initialCpbRemovalDelayLengthMinus1 = CAVLCReader.readNBit(_in, 5, "HRD: initial_cpb_removal_delay_length_minus1");
hrd.cpbRemovalDelayLengthMinus1 = CAVLCReader.readNBit(_in, 5, "HRD: cpb_removal_delay_length_minus1");
hrd.dpbOutputDelayLengthMinus1 = CAVLCReader.readNBit(_in, 5, "HRD: dpb_output_delay_length_minus1");
hrd.timeOffsetLength = CAVLCReader.readNBit(_in, 5, "HRD: time_offset_length");
return hrd;
}
public void write(ByteBuffer out) {
BitWriter writer = new BitWriter(out);
CAVLCWriter.writeNBit(writer, (long)this.profileIdc, 8, "SPS: profile_idc");
CAVLCWriter.writeBool(writer, this.constraintSet0Flag, "SPS: constraint_set_0_flag");
CAVLCWriter.writeBool(writer, this.constraintSet1Flag, "SPS: constraint_set_1_flag");
CAVLCWriter.writeBool(writer, this.constraintSet2Flag, "SPS: constraint_set_2_flag");
CAVLCWriter.writeBool(writer, this.constraintSet3Flag, "SPS: constraint_set_3_flag");
CAVLCWriter.writeBool(writer, this.constraintSet4Flag, "SPS: constraint_set_4_flag");
CAVLCWriter.writeBool(writer, this.constraintSet5Flag, "SPS: constraint_set_5_flag");
CAVLCWriter.writeNBit(writer, 0L, 2, "SPS: reserved");
CAVLCWriter.writeNBit(writer, (long)this.levelIdc, 8, "SPS: level_idc");
CAVLCWriter.writeUEtrace(writer, this.seqParameterSetId, "SPS: seq_parameter_set_id");
if (this.profileIdc == 100 || this.profileIdc == 110 || this.profileIdc == 122 || this.profileIdc == 144) {
CAVLCWriter.writeUEtrace(writer, fromColor(this.chromaFormatIdc), "SPS: chroma_format_idc");
if (this.chromaFormatIdc == ColorSpace.YUV444)
CAVLCWriter.writeBool(writer, this.separateColourPlaneFlag, "SPS: residual_color_transform_flag");
CAVLCWriter.writeUEtrace(writer, this.bitDepthLumaMinus8, "SPS: ");
CAVLCWriter.writeUEtrace(writer, this.bitDepthChromaMinus8, "SPS: ");
CAVLCWriter.writeBool(writer, this.qpprimeYZeroTransformBypassFlag, "SPS: qpprime_y_zero_transform_bypass_flag");
CAVLCWriter.writeBool(writer, (this.scalingMatrix != null), "SPS: ");
if (this.scalingMatrix != null)
for (int i = 0; i < 8; i++) {
CAVLCWriter.writeBool(writer, (this.scalingMatrix[i] != null), "SPS: ");
if (this.scalingMatrix[i] != null)
writeScalingList(writer, this.scalingMatrix, i);
}
}
CAVLCWriter.writeUEtrace(writer, this.log2MaxFrameNumMinus4, "SPS: log2_max_frame_num_minus4");
CAVLCWriter.writeUEtrace(writer, this.picOrderCntType, "SPS: pic_order_cnt_type");
if (this.picOrderCntType == 0) {
CAVLCWriter.writeUEtrace(writer, this.log2MaxPicOrderCntLsbMinus4, "SPS: log2_max_pic_order_cnt_lsb_minus4");
} else if (this.picOrderCntType == 1) {
CAVLCWriter.writeBool(writer, this.deltaPicOrderAlwaysZeroFlag, "SPS: delta_pic_order_always_zero_flag");
CAVLCWriter.writeSEtrace(writer, this.offsetForNonRefPic, "SPS: offset_for_non_ref_pic");
CAVLCWriter.writeSEtrace(writer, this.offsetForTopToBottomField, "SPS: offset_for_top_to_bottom_field");
CAVLCWriter.writeUEtrace(writer, this.offsetForRefFrame.length, "SPS: ");
for (int i = 0; i < this.offsetForRefFrame.length; i++)
CAVLCWriter.writeSEtrace(writer, this.offsetForRefFrame[i], "SPS: ");
}
CAVLCWriter.writeUEtrace(writer, this.numRefFrames, "SPS: num_ref_frames");
CAVLCWriter.writeBool(writer, this.gapsInFrameNumValueAllowedFlag, "SPS: gaps_in_frame_num_value_allowed_flag");
CAVLCWriter.writeUEtrace(writer, this.picWidthInMbsMinus1, "SPS: pic_width_in_mbs_minus1");
CAVLCWriter.writeUEtrace(writer, this.picHeightInMapUnitsMinus1, "SPS: pic_height_in_map_units_minus1");
CAVLCWriter.writeBool(writer, this.frameMbsOnlyFlag, "SPS: frame_mbs_only_flag");
if (!this.frameMbsOnlyFlag)
CAVLCWriter.writeBool(writer, this.mbAdaptiveFrameFieldFlag, "SPS: mb_adaptive_frame_field_flag");
CAVLCWriter.writeBool(writer, this.direct8x8InferenceFlag, "SPS: direct_8x8_inference_flag");
CAVLCWriter.writeBool(writer, this.frameCroppingFlag, "SPS: frame_cropping_flag");
if (this.frameCroppingFlag) {
CAVLCWriter.writeUEtrace(writer, this.frameCropLeftOffset, "SPS: frame_crop_left_offset");
CAVLCWriter.writeUEtrace(writer, this.frameCropRightOffset, "SPS: frame_crop_right_offset");
CAVLCWriter.writeUEtrace(writer, this.frameCropTopOffset, "SPS: frame_crop_top_offset");
CAVLCWriter.writeUEtrace(writer, this.frameCropBottomOffset, "SPS: frame_crop_bottom_offset");
}
CAVLCWriter.writeBool(writer, (this.vuiParams != null), "SPS: ");
if (this.vuiParams != null)
writeVUIParameters(this.vuiParams, writer);
CAVLCWriter.writeTrailingBits(writer);
}
private void writeVUIParameters(VUIParameters vuip, BitWriter writer) {
CAVLCWriter.writeBool(writer, vuip.aspectRatioInfoPresentFlag, "VUI: aspect_ratio_info_present_flag");
if (vuip.aspectRatioInfoPresentFlag) {
CAVLCWriter.writeNBit(writer, (long)vuip.aspectRatio.getValue(), 8, "VUI: aspect_ratio");
if (vuip.aspectRatio == AspectRatio.Extended_SAR) {
CAVLCWriter.writeNBit(writer, (long)vuip.sarWidth, 16, "VUI: sar_width");
CAVLCWriter.writeNBit(writer, (long)vuip.sarHeight, 16, "VUI: sar_height");
}
}
CAVLCWriter.writeBool(writer, vuip.overscanInfoPresentFlag, "VUI: overscan_info_present_flag");
if (vuip.overscanInfoPresentFlag)
CAVLCWriter.writeBool(writer, vuip.overscanAppropriateFlag, "VUI: overscan_appropriate_flag");
CAVLCWriter.writeBool(writer, vuip.videoSignalTypePresentFlag, "VUI: video_signal_type_present_flag");
if (vuip.videoSignalTypePresentFlag) {
CAVLCWriter.writeNBit(writer, (long)vuip.videoFormat, 3, "VUI: video_format");
CAVLCWriter.writeBool(writer, vuip.videoFullRangeFlag, "VUI: video_full_range_flag");
CAVLCWriter.writeBool(writer, vuip.colourDescriptionPresentFlag, "VUI: colour_description_present_flag");
if (vuip.colourDescriptionPresentFlag) {
CAVLCWriter.writeNBit(writer, (long)vuip.colourPrimaries, 8, "VUI: colour_primaries");
CAVLCWriter.writeNBit(writer, (long)vuip.transferCharacteristics, 8, "VUI: transfer_characteristics");
CAVLCWriter.writeNBit(writer, (long)vuip.matrixCoefficients, 8, "VUI: matrix_coefficients");
}
}
CAVLCWriter.writeBool(writer, vuip.chromaLocInfoPresentFlag, "VUI: chroma_loc_info_present_flag");
if (vuip.chromaLocInfoPresentFlag) {
CAVLCWriter.writeUEtrace(writer, vuip.chromaSampleLocTypeTopField, "VUI: chroma_sample_loc_type_top_field");
CAVLCWriter.writeUEtrace(writer, vuip.chromaSampleLocTypeBottomField, "VUI: chroma_sample_loc_type_bottom_field");
}
CAVLCWriter.writeBool(writer, vuip.timingInfoPresentFlag, "VUI: timing_info_present_flag");
if (vuip.timingInfoPresentFlag) {
CAVLCWriter.writeNBit(writer, (long)vuip.numUnitsInTick, 32, "VUI: num_units_in_tick");
CAVLCWriter.writeNBit(writer, (long)vuip.timeScale, 32, "VUI: time_scale");
CAVLCWriter.writeBool(writer, vuip.fixedFrameRateFlag, "VUI: fixed_frame_rate_flag");
}
CAVLCWriter.writeBool(writer, (vuip.nalHRDParams != null), "VUI: ");
if (vuip.nalHRDParams != null)
writeHRDParameters(vuip.nalHRDParams, writer);
CAVLCWriter.writeBool(writer, (vuip.vclHRDParams != null), "VUI: ");
if (vuip.vclHRDParams != null)
writeHRDParameters(vuip.vclHRDParams, writer);
if (vuip.nalHRDParams != null || vuip.vclHRDParams != null)
CAVLCWriter.writeBool(writer, vuip.lowDelayHrdFlag, "VUI: low_delay_hrd_flag");
CAVLCWriter.writeBool(writer, vuip.picStructPresentFlag, "VUI: pic_struct_present_flag");
CAVLCWriter.writeBool(writer, (vuip.bitstreamRestriction != null), "VUI: ");
if (vuip.bitstreamRestriction != null) {
CAVLCWriter.writeBool(writer, vuip.bitstreamRestriction.motionVectorsOverPicBoundariesFlag, "VUI: motion_vectors_over_pic_boundaries_flag");
CAVLCWriter.writeUEtrace(writer, vuip.bitstreamRestriction.maxBytesPerPicDenom, "VUI: max_bytes_per_pic_denom");
CAVLCWriter.writeUEtrace(writer, vuip.bitstreamRestriction.maxBitsPerMbDenom, "VUI: max_bits_per_mb_denom");
CAVLCWriter.writeUEtrace(writer, vuip.bitstreamRestriction.log2MaxMvLengthHorizontal, "VUI: log2_max_mv_length_horizontal");
CAVLCWriter.writeUEtrace(writer, vuip.bitstreamRestriction.log2MaxMvLengthVertical, "VUI: log2_max_mv_length_vertical");
CAVLCWriter.writeUEtrace(writer, vuip.bitstreamRestriction.numReorderFrames, "VUI: num_reorder_frames");
CAVLCWriter.writeUEtrace(writer, vuip.bitstreamRestriction.maxDecFrameBuffering, "VUI: max_dec_frame_buffering");
}
}
private void writeHRDParameters(HRDParameters hrd, BitWriter writer) {
CAVLCWriter.writeUEtrace(writer, hrd.cpbCntMinus1, "HRD: cpb_cnt_minus1");
CAVLCWriter.writeNBit(writer, (long)hrd.bitRateScale, 4, "HRD: bit_rate_scale");
CAVLCWriter.writeNBit(writer, (long)hrd.cpbSizeScale, 4, "HRD: cpb_size_scale");
for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpbCntMinus1; SchedSelIdx++) {
CAVLCWriter.writeUEtrace(writer, hrd.bitRateValueMinus1[SchedSelIdx], "HRD: ");
CAVLCWriter.writeUEtrace(writer, hrd.cpbSizeValueMinus1[SchedSelIdx], "HRD: ");
CAVLCWriter.writeBool(writer, hrd.cbrFlag[SchedSelIdx], "HRD: ");
}
CAVLCWriter.writeNBit(writer, (long)hrd.initialCpbRemovalDelayLengthMinus1, 5, "HRD: initial_cpb_removal_delay_length_minus1");
CAVLCWriter.writeNBit(writer, (long)hrd.cpbRemovalDelayLengthMinus1, 5, "HRD: cpb_removal_delay_length_minus1");
CAVLCWriter.writeNBit(writer, (long)hrd.dpbOutputDelayLengthMinus1, 5, "HRD: dpb_output_delay_length_minus1");
CAVLCWriter.writeNBit(writer, (long)hrd.timeOffsetLength, 5, "HRD: time_offset_length");
}
public SeqParameterSet copy() {
ByteBuffer buf = ByteBuffer.allocate(2048);
write(buf);
buf.flip();
return read(buf);
}
public int getPicOrderCntType() {
return this.picOrderCntType;
}
public boolean isFieldPicFlag() {
return this.fieldPicFlag;
}
public boolean isDeltaPicOrderAlwaysZeroFlag() {
return this.deltaPicOrderAlwaysZeroFlag;
}
public boolean isMbAdaptiveFrameFieldFlag() {
return this.mbAdaptiveFrameFieldFlag;
}
public boolean isDirect8x8InferenceFlag() {
return this.direct8x8InferenceFlag;
}
public ColorSpace getChromaFormatIdc() {
return this.chromaFormatIdc;
}
public int getLog2MaxFrameNumMinus4() {
return this.log2MaxFrameNumMinus4;
}
public int getLog2MaxPicOrderCntLsbMinus4() {
return this.log2MaxPicOrderCntLsbMinus4;
}
public int getPicHeightInMapUnitsMinus1() {
return this.picHeightInMapUnitsMinus1;
}
public int getPicWidthInMbsMinus1() {
return this.picWidthInMbsMinus1;
}
public int getBitDepthLumaMinus8() {
return this.bitDepthLumaMinus8;
}
public int getBitDepthChromaMinus8() {
return this.bitDepthChromaMinus8;
}
public boolean isQpprimeYZeroTransformBypassFlag() {
return this.qpprimeYZeroTransformBypassFlag;
}
public int getProfileIdc() {
return this.profileIdc;
}
public boolean isConstraintSet0Flag() {
return this.constraintSet0Flag;
}
public boolean isConstraintSet1Flag() {
return this.constraintSet1Flag;
}
public boolean isConstraintSet2Flag() {
return this.constraintSet2Flag;
}
public boolean isConstraintSet3Flag() {
return this.constraintSet3Flag;
}
public boolean isConstraintSet4Flag() {
return this.constraintSet4Flag;
}
public boolean isConstraintSet5Flag() {
return this.constraintSet5Flag;
}
public int getLevelIdc() {
return this.levelIdc;
}
public int getSeqParameterSetId() {
return this.seqParameterSetId;
}
public boolean isResidualColorTransformFlag() {
return this.separateColourPlaneFlag;
}
public int getOffsetForNonRefPic() {
return this.offsetForNonRefPic;
}
public int getOffsetForTopToBottomField() {
return this.offsetForTopToBottomField;
}
public int getNumRefFrames() {
return this.numRefFrames;
}
public boolean isGapsInFrameNumValueAllowedFlag() {
return this.gapsInFrameNumValueAllowedFlag;
}
public boolean isFrameMbsOnlyFlag() {
return this.frameMbsOnlyFlag;
}
public boolean isFrameCroppingFlag() {
return this.frameCroppingFlag;
}
public int getFrameCropLeftOffset() {
return this.frameCropLeftOffset;
}
public int getFrameCropRightOffset() {
return this.frameCropRightOffset;
}
public int getFrameCropTopOffset() {
return this.frameCropTopOffset;
}
public int getFrameCropBottomOffset() {
return this.frameCropBottomOffset;
}
public int[] getOffsetForRefFrame() {
return this.offsetForRefFrame;
}
public VUIParameters getVuiParams() {
return this.vuiParams;
}
public int[][] getScalingMatrix() {
return this.scalingMatrix;
}
public int getNumRefFramesInPicOrderCntCycle() {
return this.numRefFramesInPicOrderCntCycle;
}
public static int getPicHeightInMbs(SeqParameterSet sps) {
int picHeightInMbs = sps.picHeightInMapUnitsMinus1 + 1 << (sps.frameMbsOnlyFlag ? 0 : 1);
return picHeightInMbs;
}
}

View file

@ -0,0 +1,43 @@
package org.jcodec.codecs.h264.io.model;
import java.nio.ByteBuffer;
import org.jcodec.codecs.h264.decode.CAVLCReader;
import org.jcodec.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
public class SeqParameterSetExt {
public int seq_parameter_set_id;
public int aux_format_idc;
public int bit_depth_aux_minus8;
public boolean alpha_incr_flag;
public boolean additional_extension_flag;
public int alpha_opaque_value;
public int alpha_transparent_value;
public static SeqParameterSetExt read(ByteBuffer is) {
BitReader _in = BitReader.createBitReader(is);
SeqParameterSetExt spse = new SeqParameterSetExt();
spse.seq_parameter_set_id = CAVLCReader.readUEtrace(_in, "SPSE: seq_parameter_set_id");
spse.aux_format_idc = CAVLCReader.readUEtrace(_in, "SPSE: aux_format_idc");
if (spse.aux_format_idc != 0) {
spse.bit_depth_aux_minus8 = CAVLCReader.readUEtrace(_in, "SPSE: bit_depth_aux_minus8");
spse.alpha_incr_flag = CAVLCReader.readBool(_in, "SPSE: alpha_incr_flag");
spse.alpha_opaque_value = CAVLCReader.readU(_in, spse.bit_depth_aux_minus8 + 9, "SPSE: alpha_opaque_value");
spse.alpha_transparent_value = CAVLCReader.readU(_in, spse.bit_depth_aux_minus8 + 9, "SPSE: alpha_transparent_value");
}
spse.additional_extension_flag = CAVLCReader.readBool(_in, "SPSE: additional_extension_flag");
return spse;
}
public void write(ByteBuffer out) {
BitWriter writer = new BitWriter(out);
CAVLCWriter.writeTrailingBits(writer);
}
}

View file

@ -0,0 +1,67 @@
package org.jcodec.codecs.h264.io.model;
import org.jcodec.platform.Platform;
public class SliceHeader {
public SeqParameterSet sps;
public PictureParameterSet pps;
public RefPicMarking refPicMarkingNonIDR;
public RefPicMarkingIDR refPicMarkingIDR;
public int[][][] refPicReordering;
public PredictionWeightTable predWeightTable;
public int firstMbInSlice;
public boolean fieldPicFlag;
public SliceType sliceType;
public boolean sliceTypeRestr;
public int picParameterSetId;
public int frameNum;
public boolean bottomFieldFlag;
public int idrPicId;
public int picOrderCntLsb;
public int deltaPicOrderCntBottom;
public int[] deltaPicOrderCnt;
public int redundantPicCnt;
public boolean directSpatialMvPredFlag;
public boolean numRefIdxActiveOverrideFlag;
public int[] numRefIdxActiveMinus1 = new int[2];
public int cabacInitIdc;
public int sliceQpDelta;
public boolean spForSwitchFlag;
public int sliceQsDelta;
public int disableDeblockingFilterIdc;
public int sliceAlphaC0OffsetDiv2;
public int sliceBetaOffsetDiv2;
public int sliceGroupChangeCycle;
public String toString() {
return Platform.toJSON(this);
}
}

View file

@ -0,0 +1,53 @@
package org.jcodec.codecs.h264.io.model;
public final class SliceType {
private static final SliceType[] _values = new SliceType[5];
public static final SliceType P = new SliceType("P", 0);
public static final SliceType B = new SliceType("B", 1);
public static final SliceType I = new SliceType("I", 2);
public static final SliceType SP = new SliceType("SP", 3);
public static final SliceType SI = new SliceType("SI", 4);
private String _name;
private int _ordinal;
private SliceType(String name, int ordinal) {
this._name = name;
this._ordinal = ordinal;
_values[ordinal] = this;
}
public boolean isIntra() {
return (this == I || this == SI);
}
public boolean isInter() {
return (this != I && this != SI);
}
public static SliceType[] values() {
return _values;
}
public int ordinal() {
return this._ordinal;
}
public String toString() {
return this._name;
}
public String name() {
return this._name;
}
public static SliceType fromValue(int j) {
return values()[j];
}
}

View file

@ -0,0 +1,69 @@
package org.jcodec.codecs.h264.io.model;
public class VUIParameters {
public boolean aspectRatioInfoPresentFlag;
public int sarWidth;
public int sarHeight;
public boolean overscanInfoPresentFlag;
public boolean overscanAppropriateFlag;
public boolean videoSignalTypePresentFlag;
public int videoFormat;
public boolean videoFullRangeFlag;
public boolean colourDescriptionPresentFlag;
public int colourPrimaries;
public int transferCharacteristics;
public int matrixCoefficients;
public boolean chromaLocInfoPresentFlag;
public int chromaSampleLocTypeTopField;
public int chromaSampleLocTypeBottomField;
public boolean timingInfoPresentFlag;
public int numUnitsInTick;
public int timeScale;
public boolean fixedFrameRateFlag;
public boolean lowDelayHrdFlag;
public boolean picStructPresentFlag;
public HRDParameters nalHRDParams;
public HRDParameters vclHRDParams;
public BitstreamRestriction bitstreamRestriction;
public AspectRatio aspectRatio;
public static class BitstreamRestriction {
public boolean motionVectorsOverPicBoundariesFlag;
public int maxBytesPerPicDenom;
public int maxBitsPerMbDenom;
public int log2MaxMvLengthHorizontal;
public int log2MaxMvLengthVertical;
public int numReorderFrames;
public int maxDecFrameBuffering;
}
}

View file

@ -0,0 +1,74 @@
package org.jcodec.codecs.h264.io.write;
import org.jcodec.api.NotImplementedException;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.tools.Debug;
import org.jcodec.common.tools.MathUtil;
public class CAVLCWriter {
public static void writeUtrace(BitWriter out, int value, int n, String message) {
out.writeNBit(value, n);
Debug.trace(message, value);
}
public static void writeUE(BitWriter out, int value) {
int bits = 0;
int cumul = 0;
for (int i = 0; i < 15; i++) {
if (value < cumul + (1 << i)) {
bits = i;
break;
}
cumul += 1 << i;
}
out.writeNBit(0, bits);
out.write1Bit(1);
out.writeNBit(value - cumul, bits);
}
public static void writeSE(BitWriter out, int value) {
writeUE(out, MathUtil.golomb(value));
}
public static void writeUEtrace(BitWriter out, int value, String message) {
writeUE(out, value);
Debug.trace(message, value);
}
public static void writeSEtrace(BitWriter out, int value, String message) {
writeUE(out, MathUtil.golomb(value));
Debug.trace(message, value);
}
public static void writeTE(BitWriter out, int value, int max) {
if (max > 1) {
writeUE(out, value);
} else {
out.write1Bit((value ^ 0xFFFFFFFF) & 0x1);
}
}
public static void writeBool(BitWriter out, boolean value, String message) {
out.write1Bit(value ? 1 : 0);
Debug.trace(message, value ? 1 : 0);
}
public static void writeU(BitWriter out, int i, int n) {
out.writeNBit(i, n);
}
public static void writeNBit(BitWriter out, long value, int n, String message) {
for (int i = 0; i < n; i++)
out.write1Bit((int)(value >> n - i - 1) & 0x1);
Debug.trace(message, value);
}
public static void writeTrailingBits(BitWriter out) {
out.write1Bit(1);
out.flush();
}
public static void writeSliceTrailingBits() {
throw new NotImplementedException("todo");
}
}

View file

@ -0,0 +1,47 @@
package org.jcodec.codecs.h264.io.write;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.common.io.NIOUtils;
public class NALUnitWriter {
private final WritableByteChannel to;
private static ByteBuffer _MARKER = ByteBuffer.allocate(4);
static {
_MARKER.putInt(1);
_MARKER.flip();
}
public NALUnitWriter(WritableByteChannel to) {
this.to = to;
}
public void writeUnit(NALUnit nal, ByteBuffer data) throws IOException {
ByteBuffer emprev = ByteBuffer.allocate(data.remaining() + 1024);
NIOUtils.write(emprev, _MARKER);
nal.write(emprev);
emprev(emprev, data);
emprev.flip();
this.to.write(emprev);
}
private void emprev(ByteBuffer emprev, ByteBuffer data) {
ByteBuffer dd = data.duplicate();
int prev1 = 1, prev2 = 1;
while (dd.hasRemaining()) {
byte b = dd.get();
if (prev1 == 0 && prev2 == 0 && (b & 0x3) == b) {
prev2 = prev1;
prev1 = 3;
emprev.put((byte)3);
}
prev2 = prev1;
prev1 = b;
emprev.put(b);
}
}
}

View file

@ -0,0 +1,193 @@
package org.jcodec.codecs.h264.io.write;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
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.io.BitWriter;
import org.jcodec.common.model.ColorSpace;
public class SliceHeaderWriter {
public static void write(SliceHeader sliceHeader, boolean idrSlice, int nalRefIdc, BitWriter writer) {
SeqParameterSet sps = sliceHeader.sps;
PictureParameterSet pps = sliceHeader.pps;
CAVLCWriter.writeUEtrace(writer, sliceHeader.firstMbInSlice, "SH: first_mb_in_slice");
CAVLCWriter.writeUEtrace(writer, sliceHeader.sliceType.ordinal() + (sliceHeader.sliceTypeRestr ? 5 : 0), "SH: slice_type");
CAVLCWriter.writeUEtrace(writer, sliceHeader.picParameterSetId, "SH: pic_parameter_set_id");
if (sliceHeader.frameNum > 1 << sps.log2MaxFrameNumMinus4 + 4)
throw new IllegalArgumentException("frame_num > " + (1 << sps.log2MaxFrameNumMinus4 + 4));
CAVLCWriter.writeUtrace(writer, sliceHeader.frameNum, sps.log2MaxFrameNumMinus4 + 4, "SH: frame_num");
if (!sps.frameMbsOnlyFlag) {
CAVLCWriter.writeBool(writer, sliceHeader.fieldPicFlag, "SH: field_pic_flag");
if (sliceHeader.fieldPicFlag)
CAVLCWriter.writeBool(writer, sliceHeader.bottomFieldFlag, "SH: bottom_field_flag");
}
if (idrSlice)
CAVLCWriter.writeUEtrace(writer, sliceHeader.idrPicId, "SH: idr_pic_id");
if (sps.picOrderCntType == 0) {
if (sliceHeader.picOrderCntLsb > 1 << sps.log2MaxPicOrderCntLsbMinus4 + 4)
throw new IllegalArgumentException("pic_order_cnt_lsb > " + (1 << sps.log2MaxPicOrderCntLsbMinus4 + 4));
CAVLCWriter.writeU(writer, sliceHeader.picOrderCntLsb, sps.log2MaxPicOrderCntLsbMinus4 + 4);
if (pps.picOrderPresentFlag && !sps.fieldPicFlag)
CAVLCWriter.writeSEtrace(writer, sliceHeader.deltaPicOrderCntBottom, "SH: delta_pic_order_cnt_bottom");
}
if (sps.picOrderCntType == 1 && !sps.deltaPicOrderAlwaysZeroFlag) {
CAVLCWriter.writeSEtrace(writer, sliceHeader.deltaPicOrderCnt[0], "SH: delta_pic_order_cnt");
if (pps.picOrderPresentFlag && !sps.fieldPicFlag)
CAVLCWriter.writeSEtrace(writer, sliceHeader.deltaPicOrderCnt[1], "SH: delta_pic_order_cnt");
}
if (pps.redundantPicCntPresentFlag)
CAVLCWriter.writeUEtrace(writer, sliceHeader.redundantPicCnt, "SH: redundant_pic_cnt");
if (sliceHeader.sliceType == SliceType.B)
CAVLCWriter.writeBool(writer, sliceHeader.directSpatialMvPredFlag, "SH: direct_spatial_mv_pred_flag");
if (sliceHeader.sliceType == SliceType.P || sliceHeader.sliceType == SliceType.SP || sliceHeader.sliceType == SliceType.B) {
CAVLCWriter.writeBool(writer, sliceHeader.numRefIdxActiveOverrideFlag, "SH: num_ref_idx_active_override_flag");
if (sliceHeader.numRefIdxActiveOverrideFlag) {
CAVLCWriter.writeUEtrace(writer, sliceHeader.numRefIdxActiveMinus1[0], "SH: num_ref_idx_l0_active_minus1");
if (sliceHeader.sliceType == SliceType.B)
CAVLCWriter.writeUEtrace(writer, sliceHeader.numRefIdxActiveMinus1[1], "SH: num_ref_idx_l1_active_minus1");
}
}
writeRefPicListReordering(sliceHeader, writer);
if ((pps.weightedPredFlag && (sliceHeader.sliceType == SliceType.P || sliceHeader.sliceType == SliceType.SP)) || (pps.weightedBipredIdc == 1 && sliceHeader.sliceType == SliceType.B))
writePredWeightTable(sliceHeader, writer);
if (nalRefIdc != 0)
writeDecRefPicMarking(sliceHeader, idrSlice, writer);
if (pps.entropyCodingModeFlag && sliceHeader.sliceType.isInter())
CAVLCWriter.writeUEtrace(writer, sliceHeader.cabacInitIdc, "SH: cabac_init_idc");
CAVLCWriter.writeSEtrace(writer, sliceHeader.sliceQpDelta, "SH: slice_qp_delta");
if (sliceHeader.sliceType == SliceType.SP || sliceHeader.sliceType == SliceType.SI) {
if (sliceHeader.sliceType == SliceType.SP)
CAVLCWriter.writeBool(writer, sliceHeader.spForSwitchFlag, "SH: sp_for_switch_flag");
CAVLCWriter.writeSEtrace(writer, sliceHeader.sliceQsDelta, "SH: slice_qs_delta");
}
if (pps.deblockingFilterControlPresentFlag) {
CAVLCWriter.writeUEtrace(writer, sliceHeader.disableDeblockingFilterIdc, "SH: disable_deblocking_filter_idc");
if (sliceHeader.disableDeblockingFilterIdc != 1) {
CAVLCWriter.writeSEtrace(writer, sliceHeader.sliceAlphaC0OffsetDiv2, "SH: slice_alpha_c0_offset_div2");
CAVLCWriter.writeSEtrace(writer, sliceHeader.sliceBetaOffsetDiv2, "SH: slice_beta_offset_div2");
}
}
if (pps.numSliceGroupsMinus1 > 0 && pps.sliceGroupMapType >= 3 && pps.sliceGroupMapType <= 5) {
int len = (sps.picHeightInMapUnitsMinus1 + 1) * (sps.picWidthInMbsMinus1 + 1) / (pps.sliceGroupChangeRateMinus1 + 1);
if ((sps.picHeightInMapUnitsMinus1 + 1) * (sps.picWidthInMbsMinus1 + 1) % (pps.sliceGroupChangeRateMinus1 + 1) > 0)
len++;
len = CeilLog2(len + 1);
CAVLCWriter.writeU(writer, sliceHeader.sliceGroupChangeCycle, len);
}
}
private static int CeilLog2(int uiVal) {
int uiTmp = uiVal - 1;
int uiRet = 0;
while (uiTmp != 0) {
uiTmp >>= 1;
uiRet++;
}
return uiRet;
}
private static void writeDecRefPicMarking(SliceHeader sliceHeader, boolean idrSlice, BitWriter writer) {
if (idrSlice) {
RefPicMarkingIDR drpmidr = sliceHeader.refPicMarkingIDR;
CAVLCWriter.writeBool(writer, drpmidr.isDiscardDecodedPics(), "SH: no_output_of_prior_pics_flag");
CAVLCWriter.writeBool(writer, drpmidr.isUseForlongTerm(), "SH: long_term_reference_flag");
} else {
CAVLCWriter.writeBool(writer, (sliceHeader.refPicMarkingNonIDR != null), "SH: adaptive_ref_pic_marking_mode_flag");
if (sliceHeader.refPicMarkingNonIDR != null) {
RefPicMarking drpmidr = sliceHeader.refPicMarkingNonIDR;
RefPicMarking.Instruction[] instructions = drpmidr.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction mmop = instructions[i];
switch (mmop.getType()) {
case REMOVE_SHORT:
CAVLCWriter.writeUEtrace(writer, 1, "SH: memory_management_control_operation");
CAVLCWriter.writeUEtrace(writer, mmop.getArg1() - 1, "SH: difference_of_pic_nums_minus1");
break;
case REMOVE_LONG:
CAVLCWriter.writeUEtrace(writer, 2, "SH: memory_management_control_operation");
CAVLCWriter.writeUEtrace(writer, mmop.getArg1(), "SH: long_term_pic_num");
break;
case CONVERT_INTO_LONG:
CAVLCWriter.writeUEtrace(writer, 3, "SH: memory_management_control_operation");
CAVLCWriter.writeUEtrace(writer, mmop.getArg1() - 1, "SH: difference_of_pic_nums_minus1");
CAVLCWriter.writeUEtrace(writer, mmop.getArg2(), "SH: long_term_frame_idx");
break;
case TRUNK_LONG:
CAVLCWriter.writeUEtrace(writer, 4, "SH: memory_management_control_operation");
CAVLCWriter.writeUEtrace(writer, mmop.getArg1() + 1, "SH: max_long_term_frame_idx_plus1");
break;
case CLEAR:
CAVLCWriter.writeUEtrace(writer, 5, "SH: memory_management_control_operation");
break;
case MARK_LONG:
CAVLCWriter.writeUEtrace(writer, 6, "SH: memory_management_control_operation");
CAVLCWriter.writeUEtrace(writer, mmop.getArg1(), "SH: long_term_frame_idx");
break;
}
}
CAVLCWriter.writeUEtrace(writer, 0, "SH: memory_management_control_operation");
}
}
}
private static void writePredWeightTable(SliceHeader sliceHeader, BitWriter writer) {
SeqParameterSet sps = sliceHeader.sps;
CAVLCWriter.writeUEtrace(writer, sliceHeader.predWeightTable.lumaLog2WeightDenom, "SH: luma_log2_weight_denom");
if (sps.chromaFormatIdc != ColorSpace.MONO)
CAVLCWriter.writeUEtrace(writer, sliceHeader.predWeightTable.chromaLog2WeightDenom, "SH: chroma_log2_weight_denom");
writeOffsetWeight(sliceHeader, writer, 0);
if (sliceHeader.sliceType == SliceType.B)
writeOffsetWeight(sliceHeader, writer, 1);
}
private static void writeOffsetWeight(SliceHeader sliceHeader, BitWriter writer, int list) {
SeqParameterSet sps = sliceHeader.sps;
int defaultLW = 1 << sliceHeader.predWeightTable.lumaLog2WeightDenom;
int defaultCW = 1 << sliceHeader.predWeightTable.chromaLog2WeightDenom;
for (int i = 0; i < (sliceHeader.predWeightTable.lumaWeight[list]).length; i++) {
boolean flagLuma = (sliceHeader.predWeightTable.lumaWeight[list][i] != defaultLW || sliceHeader.predWeightTable.lumaOffset[list][i] != 0);
CAVLCWriter.writeBool(writer, flagLuma, "SH: luma_weight_l0_flag");
if (flagLuma) {
CAVLCWriter.writeSEtrace(writer, sliceHeader.predWeightTable.lumaWeight[list][i], "SH: luma_weight_l" + list);
CAVLCWriter.writeSEtrace(writer, sliceHeader.predWeightTable.lumaOffset[list][i], "SH: luma_offset_l" + list);
}
if (sps.chromaFormatIdc != ColorSpace.MONO) {
boolean flagChroma = (sliceHeader.predWeightTable.chromaWeight[list][0][i] != defaultCW || sliceHeader.predWeightTable.chromaOffset[list][0][i] != 0 || sliceHeader.predWeightTable.chromaWeight[list][1][i] != defaultCW || sliceHeader.predWeightTable.chromaOffset[list][1][i] != 0);
CAVLCWriter.writeBool(writer, flagChroma, "SH: chroma_weight_l0_flag");
if (flagChroma)
for (int j = 0; j < 2; j++) {
CAVLCWriter.writeSEtrace(writer, sliceHeader.predWeightTable.chromaWeight[list][j][i], "SH: chroma_weight_l" + list);
CAVLCWriter.writeSEtrace(writer, sliceHeader.predWeightTable.chromaOffset[list][j][i], "SH: chroma_offset_l" + list);
}
}
}
}
private static void writeRefPicListReordering(SliceHeader sliceHeader, BitWriter writer) {
if (sliceHeader.sliceType.isInter()) {
boolean l0ReorderingPresent = (sliceHeader.refPicReordering != null && sliceHeader.refPicReordering[0] != null);
CAVLCWriter.writeBool(writer, l0ReorderingPresent, "SH: ref_pic_list_reordering_flag_l0");
if (l0ReorderingPresent)
writeReorderingList(sliceHeader.refPicReordering[0], writer);
}
if (sliceHeader.sliceType == SliceType.B) {
boolean l1ReorderingPresent = (sliceHeader.refPicReordering != null && sliceHeader.refPicReordering[1] != null);
CAVLCWriter.writeBool(writer, l1ReorderingPresent, "SH: ref_pic_list_reordering_flag_l1");
if (l1ReorderingPresent)
writeReorderingList(sliceHeader.refPicReordering[1], writer);
}
}
private static void writeReorderingList(int[][] reordering, BitWriter writer) {
if (reordering == null)
return;
for (int i = 0; i < (reordering[0]).length; i++) {
CAVLCWriter.writeUEtrace(writer, reordering[0][i], "SH: reordering_of_pic_nums_idc");
CAVLCWriter.writeUEtrace(writer, reordering[1][i], "SH: abs_diff_pic_num_minus1");
}
CAVLCWriter.writeUEtrace(writer, 3, "SH: reordering_of_pic_nums_idc");
}
}

View file

@ -0,0 +1,128 @@
package org.jcodec.codecs.h264.mp4;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.common.Preconditions;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.Header;
public class AvcCBox extends Box {
private int profile;
private int profileCompat;
private int level;
private int nalLengthSize;
private List<ByteBuffer> spsList;
private List<ByteBuffer> ppsList;
public AvcCBox(Header header) {
super(header);
this.spsList = new ArrayList<>();
this.ppsList = new ArrayList<>();
}
public static String fourcc() {
return "avcC";
}
public static AvcCBox parseAvcCBox(ByteBuffer buf) {
AvcCBox avcCBox = new AvcCBox(new Header(fourcc()));
avcCBox.parse(buf);
return avcCBox;
}
public static AvcCBox createEmpty() {
return new AvcCBox(new Header(fourcc()));
}
public static AvcCBox createAvcCBox(int profile, int profileCompat, int level, int nalLengthSize, List<ByteBuffer> spsList, List<ByteBuffer> ppsList) {
AvcCBox avcc = new AvcCBox(new Header(fourcc()));
avcc.profile = profile;
avcc.profileCompat = profileCompat;
avcc.level = level;
avcc.nalLengthSize = nalLengthSize;
avcc.spsList = spsList;
avcc.ppsList = ppsList;
return avcc;
}
public void parse(ByteBuffer input) {
NIOUtils.skip(input, 1);
this.profile = input.get() & 0xFF;
this.profileCompat = input.get() & 0xFF;
this.level = input.get() & 0xFF;
int flags = input.get() & 0xFF;
this.nalLengthSize = (flags & 0x3) + 1;
int nSPS = input.get() & 0x1F;
for (int i = 0; i < nSPS; i++) {
int spsSize = input.getShort();
Preconditions.checkState((39 == (input.get() & 0x3F)));
this.spsList.add(NIOUtils.read(input, spsSize - 1));
}
int nPPS = input.get() & 0xFF;
for (int j = 0; j < nPPS; j++) {
int ppsSize = input.getShort();
Preconditions.checkState((40 == (input.get() & 0x3F)));
this.ppsList.add(NIOUtils.read(input, ppsSize - 1));
}
}
public void doWrite(ByteBuffer out) {
out.put((byte)1);
out.put((byte)this.profile);
out.put((byte)this.profileCompat);
out.put((byte)this.level);
out.put((byte)-1);
out.put((byte)(this.spsList.size() | 0xE0));
for (ByteBuffer sps : this.spsList) {
out.putShort((short)(sps.remaining() + 1));
out.put((byte)103);
NIOUtils.write(out, sps);
}
out.put((byte)this.ppsList.size());
for (ByteBuffer pps : this.ppsList) {
out.putShort((short)(byte)(pps.remaining() + 1));
out.put((byte)104);
NIOUtils.write(out, pps);
}
}
public int estimateSize() {
int sz = 17;
for (ByteBuffer sps : this.spsList)
sz += 3 + sps.remaining();
for (ByteBuffer pps : this.ppsList)
sz += 3 + pps.remaining();
return sz;
}
public int getProfile() {
return this.profile;
}
public int getProfileCompat() {
return this.profileCompat;
}
public int getLevel() {
return this.level;
}
public List<ByteBuffer> getSpsList() {
return this.spsList;
}
public List<ByteBuffer> getPpsList() {
return this.ppsList;
}
public int getNalLengthSize() {
return this.nalLengthSize;
}
}