package org.jcodec.codecs.h264.mp4;
import org.jcodec.common.Assert;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.Header;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Creates MP4 file out of a set of samples
*
* @author The JCodec project
*
*/
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<ByteBuffer>();
this.ppsList = new ArrayList<ByteBuffer>();
}
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;
}
@Override
public void parse(ByteBuffer input) {
NIOUtils.skip(input, 1);
profile = input.get() & 0xff;
profileCompat = input.get() & 0xff;
level = input.get() & 0xff;
int flags = input.get() & 0xff;
nalLengthSize = (flags & 0x03) + 1;
int nSPS = input.get() & 0x1f; // 3 bits reserved + 5 bits number of
// sps
for (int i = 0; i < nSPS; i++) {
int spsSize = input.getShort();
Assert.assertEquals(0x27, input.get() & 0x3f);
spsList.add(NIOUtils.read(input, spsSize - 1));
}
int nPPS = input.get() & 0xff;
for (int i = 0; i < nPPS; i++) {
int ppsSize = input.getShort();
Assert.assertEquals(0x28, input.get() & 0x3f);
ppsList.add(NIOUtils.read(input, ppsSize - 1));
}
}
@Override
public void doWrite(ByteBuffer out) {
out.put((byte) 0x1); // version
out.put((byte) profile);
out.put((byte) profileCompat);
out.put((byte) level);
out.put((byte) 0xff);
out.put((byte) (spsList.size() | 0xe0));
for (ByteBuffer sps : spsList) {
out.putShort((short) (sps.remaining() + 1));
out.put((byte) 0x67);
NIOUtils.write(out, sps);
}
out.put((byte) ppsList.size());
for (ByteBuffer pps : ppsList) {
out.putShort((byte) (pps.remaining() + 1));
out.put((byte) 0x68);
NIOUtils.write(out, pps);
}
}
public int getProfile() {
return profile;
}
public int getProfileCompat() {
return profileCompat;
}
public int getLevel() {
return level;
}
public List<ByteBuffer> getSpsList() {
return spsList;
}
public List<ByteBuffer> getPpsList() {
return ppsList;
}
public int getNalLengthSize() {
return nalLengthSize;
}
// public void toNAL(ByteBuffer codecPrivate) {
// H264Utils.toNAL(codecPrivate, getSpsList(), getPpsList());
// }
//
// public ByteBuffer toNAL() {
// ByteBuffer bb = ByteBuffer.allocate(2048);
// H264Utils.toNAL(bb, getSpsList(), getPpsList());
// bb.flip();
// return bb;
// }
//
// public static AvcCBox fromNAL(ByteBuffer codecPrivate) {
// List<ByteBuffer> spsList = new ArrayList<ByteBuffer>();
// List<ByteBuffer> ppsList = new ArrayList<ByteBuffer>();
//
// ByteBuffer dup = codecPrivate.duplicate();
//
// ByteBuffer buf;
// SeqParameterSet sps = null;
// while ((buf = H264Utils.nextNALUnit(dup)) != null) {
// NALUnit nu = NALUnit.read(buf);
//
// H264Utils.unescapeNAL(buf);
//
// if (nu.type == NALUnitType.PPS) {
// ppsList.add(buf);
// } else if (nu.type == NALUnitType.SPS) {
// spsList.add(buf);
// sps = SeqParameterSet.read(buf.duplicate());
// }
// }
// if (spsList.size() == 0 || ppsList.size() == 0)
// return null;
// return new AvcCBox(sps.profile_idc, 0, sps.level_idc, spsList, ppsList);
// }
}