package org.jcodec.codecs.prores;
import static org.jcodec.codecs.mpeg12.MPEGConst.BLOCK_TO_CC;
import static org.jcodec.common.model.ColorSpace.YUV422_10;
import java.nio.ByteBuffer;
import org.jcodec.codecs.mpeg12.MPEGDecoder;
import org.jcodec.codecs.mpeg12.bitstream.GOPHeader;
import org.jcodec.codecs.mpeg12.bitstream.SequenceHeader;
import org.jcodec.codecs.prores.ProresEncoder.Profile;
import org.jcodec.common.dct.DCTRef;
import org.jcodec.common.dct.SimpleIDCT10Bit;
import org.jcodec.common.model.Picture;
import org.jcodec.scale.ColorUtil;
import org.jcodec.scale.Transform;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Converts MPEG 2 I-frame to a ProRes frame without re-transcoding ( without
* inverse and forward DCT step and frame reconstruction ).
*
* This is possible due to MPEG 2 and ProRes use similar DCT 8x8 based
* compression with macroblocks of 16x16 pixels
*
* @author The JCodec project
*
*/
public class Mpeg2Prores extends MPEGDecoder {
private DCT2Prores dct2Prores;
public Mpeg2Prores(SequenceHeader sh, GOPHeader gh, Profile profile) {
super(sh, gh);
dct2Prores = new DCT2Prores(profile);
}
protected void idctPut(int[] block, int[][] buf, int stride, int chromaFormat, int blkNo, int mbX, int mbY,
int dctType) {
int mbAddr = mbY * (stride >> 4) + mbX;
int off = blkNo < 4 ? ((mbAddr << 8) + (blkNo << 6))
: ((mbAddr << (5 + chromaFormat)) + (((blkNo - 4) >> 1) << 6));
System.arraycopy(block, 0, buf[BLOCK_TO_CC[blkNo]], off, 64);
buf[3][mbAddr] = dctType;
}
public ByteBuffer transcode(ByteBuffer in, ByteBuffer _out) {
ByteBuffer out = _out.slice();
int width = (sh.horizontal_size + 15) & ~0xf;
int height = (sh.vertical_size + 15) & ~0xf;
int[][] buffer = new int[][] { new int[width * height], new int[width * height], new int[width * height],
new int[(width >> 4) * (height >> 4)] };
Picture dct = decodeFrame(in, buffer);
Picture[] pic = convert(dct);
if (pic.length == 1)
dct2Prores.encodeFrame(out, pic[0]);
else
dct2Prores.encodeFrame(out, pic[0], pic[1]);
out.flip();
return out;
}
private Picture[] convert(Picture dct) {
int nInterlaced = 0;
for (int i : dct.getPlaneData(3)) {
nInterlaced += i;
}
Picture[] result;
if (nInterlaced == 0) {
upShift(dct);
result = new Picture[] { colorCvt(dct) };
} /*
* else if (nInterlaced < dct.getDctTypes().length / 10) { result =
* new Picture[] { extend(progressive(dct)) }; }
*/else {
Picture[] field = interlaced(dct);
result = new Picture[] { colorCvt(field[0]), colorCvt(field[1]) };
}
System.out.println(nInterlaced);
return result;
}
private void upShift(Picture dct) {
for (int[] is : dct.getData()) {
upShift(is, 0, is.length);
}
}
private Picture[] interlaced(Picture dct) {
int mbWidth = (dct.getWidth() + 15) >> 4;
int mbHeight = (dct.getHeight() + 15) >> 4;
Picture field1 = Picture.create(dct.getWidth(), dct.getHeight() >> 1, dct.getColor());
Picture field2 = Picture.create(dct.getWidth(), dct.getHeight() >> 1, dct.getColor());
splitY(mbWidth, mbHeight, dct.getPlaneData(0), field1.getPlaneData(0), field2.getPlaneData(0),
dct.getPlaneData(3));
splitCbCr(mbWidth, mbHeight, dct.getPlaneData(1), field1.getPlaneData(1), field2.getPlaneData(1),
dct.getPlaneData(3));
splitCbCr(mbWidth, mbHeight, dct.getPlaneData(2), field1.getPlaneData(2), field2.getPlaneData(2),
dct.getPlaneData(3));
return new Picture[] { field1, field2 };
}
private final void splitY(int mbWidth, int mbHeight, int[] y, int[] y1, int[] y2, int[] dctTypes) {
int dstOff = 0, srcOff = 0, i = 0;
for (int mbY = 0; mbY < mbHeight; mbY++) {
for (int mbX = 0; mbX < mbWidth; mbX++, i++, dstOff += 256, srcOff += 256) {
if (dctTypes[i] == 0) {
SimpleIDCT10Bit.idct10(y, srcOff);
SimpleIDCT10Bit.idct10(y, srcOff + 64);
SimpleIDCT10Bit.idct10(y, srcOff + 128);
SimpleIDCT10Bit.idct10(y, srcOff + 192);
deinterleave(y, srcOff, srcOff + 128, y1, y2, dstOff);
deinterleave(y, srcOff + 64, srcOff + 192, y1, y2, dstOff + 64);
DCTRef.fdct(y1, dstOff);
DCTRef.fdct(y1, dstOff + 64);
DCTRef.fdct(y2, dstOff);
DCTRef.fdct(y2, dstOff + 64);
} else {
copyShift(y, srcOff, y1, dstOff, 128);
copyShift(y, srcOff + 128, y2, dstOff, 128);
}
}
if ((mbY & 0x1) == 0)
dstOff -= (mbWidth << 8) - 128;
else
dstOff -= 128;
}
}
private final void copyShift(int[] src, int srcOff, int[] dst, int dstOff, int len) {
for (int i = 0; i < len; i++)
src[srcOff++] = dst[dstOff++] << 2;
}
private final void splitCbCr(int mbWidth, int mbHeight, int[] y, int[] y1, int[] y2, int[] dctTypes) {
int dstOff = 0, srcOff = 0, i = 0;
for (int mbY = 0; mbY < mbHeight; mbY++) {
for (int mbX = 0; mbX < mbWidth; mbX++, i++, dstOff += 128, srcOff += 128) {
if (dctTypes[i] == 0) {
SimpleIDCT10Bit.idct10(y, srcOff);
SimpleIDCT10Bit.idct10(y, srcOff + 64);
deinterleave(y, srcOff, srcOff + 64, y1, y2, dstOff);
DCTRef.fdct(y1, dstOff);
DCTRef.fdct(y2, dstOff);
} else {
copyShift(y, srcOff, y1, dstOff, 64);
copyShift(y, srcOff + 64, y2, dstOff, 64);
}
}
if ((mbY & 0x1) == 0)
dstOff -= (mbWidth << 7) - 64;
else
dstOff -= 64;
}
}
private void deinterleave(int[] y, int topOff, int botOff, int[] y1, int[] y2, int blkOff) {
copyLine(y, y1, topOff + 0, blkOff + 0);
copyLine(y, y1, topOff + 16, blkOff + 8);
copyLine(y, y1, topOff + 32, blkOff + 16);
copyLine(y, y1, topOff + 48, blkOff + 24);
copyLine(y, y1, botOff + 0, blkOff + 32);
copyLine(y, y1, botOff + 16, blkOff + 40);
copyLine(y, y1, botOff + 32, blkOff + 48);
copyLine(y, y1, botOff + 48, blkOff + 56);
copyLine(y, y2, topOff + 8, blkOff + 0);
copyLine(y, y2, topOff + 24, blkOff + 8);
copyLine(y, y2, topOff + 40, blkOff + 16);
copyLine(y, y2, topOff + 56, blkOff + 24);
copyLine(y, y2, botOff + 8, blkOff + 32);
copyLine(y, y2, botOff + 24, blkOff + 40);
copyLine(y, y2, botOff + 40, blkOff + 48);
copyLine(y, y2, botOff + 56, blkOff + 56);
}
private Picture progressive(Picture dct) {
progressiveY(dct.getPlaneData(0), dct.getPlaneData(3));
progressiveCbCr(dct.getPlaneData(0), dct.getPlaneData(3));
progressiveCbCr(dct.getPlaneData(0), dct.getPlaneData(3));
return dct;
}
private void progressiveY(int[] y, int[] dctTypes) {
for (int i = 0; i < dctTypes.length; i++) {
if (dctTypes[i] == 1) {
SimpleIDCT10Bit.idct10(y, (i << 8) + 0);
SimpleIDCT10Bit.idct10(y, (i << 8) + 64);
SimpleIDCT10Bit.idct10(y, (i << 8) + 128);
SimpleIDCT10Bit.idct10(y, (i << 8) + 192);
interleave(y, (i << 8) + 0, (i << 8) + 128);
interleave(y, (i << 8) + 64, (i << 8) + 192);
DCTRef.fdct(y, (i << 8) + 0);
DCTRef.fdct(y, (i << 8) + 64);
DCTRef.fdct(y, (i << 8) + 128);
DCTRef.fdct(y, (i << 8) + 192);
} else {
upShift(y, i << 8, 256);
}
}
}
private void upShift(int[] y, int off, int len) {
for (int i = 0; i < len; i++)
y[off++] <<= 2;
}
private void progressiveCbCr(int[] y, int[] dctTypes) {
for (int i = 0; i < dctTypes.length; i++) {
if (dctTypes[i] == 1) {
SimpleIDCT10Bit.idct10(y, (i << 7) + 0);
SimpleIDCT10Bit.idct10(y, (i << 7) + 64);
interleave(y, (i << 7) + 0, (i << 7) + 64);
DCTRef.fdct(y, (i << 7) + 0);
DCTRef.fdct(y, (i << 7) + 64);
} else {
upShift(y, i << 7, 128);
}
}
}
private void interleave(int[] y, int off1, int off2) {
int[] tmp = new int[64];
for (int i = 0; i < 64; i++)
tmp[i] = y[off2 + i];
copyLine(y, y, off1 + 56, off2 + 48);
copyLine(y, y, off1 + 48, off2 + 32);
copyLine(y, y, off1 + 40, off2 + 16);
copyLine(y, y, off1 + 32, off2);
copyLine(y, y, off1 + 24, off1 + 48);
copyLine(y, y, off1 + 16, off1 + 32);
copyLine(y, y, off1 + 8, off1 + 16);
copyLine(tmp, y, 0, off1 + 8);
copyLine(tmp, y, 8, off1 + 24);
copyLine(tmp, y, 16, off1 + 40);
copyLine(tmp, y, 24, off1 + 56);
copyLine(tmp, y, 32, off2 + 8);
copyLine(tmp, y, 40, off2 + 24);
copyLine(tmp, y, 48, off2 + 40);
}
private final void copyLine(int[] from, int[] to, int offFrom, int offTo) {
for (int i = 0; i < 8; i++)
to[offTo++] = from[offFrom++];
}
private Picture colorCvt(Picture in) {
Picture out;
if (in.getColor() == YUV422_10) {
out = in;
} else {
Transform trans = ColorUtil.getTransform(in.getColor(), YUV422_10);
out = Picture.create(in.getWidth(), in.getHeight(), YUV422_10);
trans.transform(in, out);
}
return out;
}
}