package org.jcodec.codecs.h264.encode;
import static org.jcodec.codecs.h264.H264Const.BLK_INV_MAP;
import static org.jcodec.codecs.h264.H264Const.BLK_X;
import static org.jcodec.codecs.h264.H264Const.BLK_Y;
import static org.jcodec.codecs.h264.H264Const.MB_BLK_OFF_LEFT;
import static org.jcodec.codecs.h264.H264Const.MB_BLK_OFF_TOP;
import static org.jcodec.codecs.h264.decode.CoeffTransformer.reorderDC4x4;
import static org.jcodec.codecs.h264.io.model.MBType.I_16x16;
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.io.BitWriter;
import org.jcodec.common.model.Picture8Bit;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Encodes macroblock as I16x16
*
* @author Stanislav Vitvitskyy
*/
public class MBEncoderI16x16 {
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 encodeMacroblock(Picture8Bit pic, int mbX, int mbY, BitWriter out, EncodedMB outMB,
EncodedMB leftOutMB, EncodedMB topOutMB, int qp, int qpDelta) {
CAVLCWriter.writeUE(out, 0); // Chroma prediction mode -- DC
CAVLCWriter.writeSE(out, qpDelta); // MB QP delta
outMB.setType(MBType.I_16x16);
outMB.setQp(qp);
luma(pic, mbX, mbY, out, qp, outMB.getPixels(), 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(Picture8Bit pic, int mbX, int mbY, BitWriter out, int qp, Picture8Bit outMB) {
int cw = pic.getColor().compWidth[1];
int ch = pic.getColor().compHeight[1];
int x = mbX << (4 - cw);
int y = mbY << (4 - ch);
int[][] ac1 = new int[16 >> (cw + ch)][16];
int[][] ac2 = new int[16 >> (cw + ch)][16];
byte[][] pred1 = new byte[16 >> (cw + ch)][16];
byte[][] pred2 = new byte[16 >> (cw + ch)][16];
predictChroma(pic, ac1, pred1, 1, cw, ch, x, y);
predictChroma(pic, ac2, pred2, 2, cw, ch, x, y);
chromaResidual(pic, mbX, mbY, out, qp, ac1, ac2, cavlc[1], cavlc[2], I_16x16, I_16x16);
putChroma(outMB.getData()[1], 1, x, y, ac1, pred1);
putChroma(outMB.getData()[2], 2, x, y, ac2, pred2);
}
public static void chromaResidual(Picture8Bit 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(Picture8Bit pic, int mbX, int mbY, BitWriter out, int qp, Picture8Bit 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, I_16x16, I_16x16);
writeAC(cavlc, mbX, mbY, out, mbX << 2, mbY << 2, ac, qp, I_16x16, 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, BLK_X[blk], 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);
} else if (dc.length == 8) {
CoeffTransformer.invDC4x2(dc);
CoeffTransformer.dequantizeDC4x2(dc, qp);
} else {
CoeffTransformer.invDC4x4(dc);
CoeffTransformer.dequantizeDC4x4(dc, qp);
reorderDC4x4(dc);
}
for (int i = 0; i < ac.length; i++) {
CoeffTransformer.dequantizeAC(ac[i], qp);
ac[i][0] = dc[i];
CoeffTransformer.idct4x4(ac[i]);
// for(int j = 0; j < ac[i].length; j++)
// ac[i][j] -= 128;
}
}
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[BLK_INV_MAP[i]] = CAVLC.totalCoeff(cavlc.writeACBlock(out, mbLeftBlk + MB_BLK_OFF_LEFT[i], mbTopBlk
+ 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 {
reorderDC4x4(dc);
CoeffTransformer.quantizeDC4x4(dc, qp);
CoeffTransformer.fvdDC4x4(dc);
// TODO: calc here
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++) {
// shift back up
// System.out.print("Chroma: ");
// for (int j = 0; j < ac[i].length; j++) {
// ac[i][j] += 128;
// System.out.print(ac[i][j] + ",");
// }
// System.out.println();
CoeffTransformer.fdct4x4(ac[i]);
}
}
private void predictChroma(Picture8Bit pic, int[][] ac, byte[][] pred, int comp, int cw, int ch, 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, predY = y & 0x7;
if (x != 0 && y != 0)
dc = chromaPredTwo(leftRow[comp], topLine[comp], predY, x);
else if (x != 0)
dc = chromaPredOne(leftRow[comp], predY);
else if (y != 0)
dc = chromaPredOne(topLine[comp], x);
else
dc = 0;
for (int i = 0; i < pred.length; i++)
pred[i] += dc;
}
private void chromaPredBlk1(int comp, int x, int y, byte[] pred) {
int dc, predY = y & 0x7;
if (y != 0)
dc = chromaPredOne(topLine[comp], x + 4);
else if (x != 0)
dc = chromaPredOne(leftRow[comp], predY);
else
dc = 0;
for (int i = 0; i < pred.length; i++)
pred[i] += dc;
}
private void chromaPredBlk2(int comp, int x, int y, byte[] pred) {
int dc, predY = y & 0x7;
if (x != 0)
dc = chromaPredOne(leftRow[comp], predY + 4);
else if (y != 0)
dc = chromaPredOne(topLine[comp], x);
else
dc = 0;
for (int i = 0; i < pred.length; i++)
pred[i] += dc;
}
private void chromaPredBlk3(int comp, int x, int y, byte[] pred) {
int dc, predY = y & 0x7;
if (x != 0 && y != 0)
dc = chromaPredTwo(leftRow[comp], topLine[comp], predY + 4, x + 4);
else if (x != 0)
dc = chromaPredOne(leftRow[comp], predY + 4);
else if (y != 0)
dc = chromaPredOne(topLine[comp], x + 4);
else
dc = 0;
for (int i = 0; i < pred.length; i++)
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(leftRow[0]) + 8) >> 4;
else if (x == 0)
dc = (ArrayUtil.sumByte3(topLine[0], x, 16) + 8) >> 4;
else
dc = (ArrayUtil.sumByte(leftRow[0]) + ArrayUtil.sumByte3(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] += dc;
}
private void transform(Picture8Bit 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
+ BLK_X[i], y + BLK_Y[i], coeff, pred[i], 4, 4);
// shift back up
// System.out.print("Luma: ");
// for (int j = 0; j < coeff.length; j++) {
// coeff[j] += 128;
// System.out.print(coeff[j] + ",");
// }
// System.out.println();
CoeffTransformer.fdct4x4(coeff);
}
}
public int getPredMode(Picture8Bit pic, int mbX, int mbY) {
return 2;
}
public int getCbpChroma(Picture8Bit pic, int mbX, int mbY) {
return 2;
}
public int getCbpLuma(Picture8Bit pic, int mbX, int mbY) {
return 15;
}
}