package org.jcodec.codecs.h264;
import static org.jcodec.codecs.h264.io.model.NALUnitType.IDR_SLICE;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.RefPicMarking.InstrType;
import org.jcodec.codecs.h264.io.model.RefPicMarking.Instruction;
import org.jcodec.codecs.h264.io.model.SliceHeader;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* POC ( Picture Order Count ) manager
*
* Picture Order Count is used to represent an order of picture in a GOP ( Group
* of Pictures ) this is needed to correctly reorder and B-framed GOPs. POC is
* also used when building lists of reference pictures ( see 8.2.4.2 ).
*
* There are 3 possible ways of assigning POC to decoded pictures:
*
* - Explicit, i.e. POC is directly specified in a slice header in form <POC
* Pred> + <POC Dec>. <POC Pred> is a significant part of POC ( see 8.2.1.1 ). -
* Frame based type 1 ( see 8.2.1.2 ). - Frame based type 2 ( see 8.2.1.3 ).
*
* @author The JCodec project
*
*/
public class POCManager {
private int prevPOCMsb;
private int prevPOCLsb;
public int calcPOC(SliceHeader firstSliceHeader, NALUnit firstNu) {
switch (firstSliceHeader.sps.pic_order_cnt_type) {
case 0:
return calcPOC0(firstSliceHeader, firstNu);
case 1:
return calcPOC1(firstSliceHeader, firstNu);
case 2:
return calcPOC2(firstSliceHeader, firstNu);
default:
throw new RuntimeException("POC no!!!");
}
}
private int calcPOC2(SliceHeader firstSliceHeader, NALUnit firstNu) {
return firstSliceHeader.frame_num << 1;
}
private int calcPOC1(SliceHeader firstSliceHeader, NALUnit firstNu) {
return firstSliceHeader.frame_num << 1;
}
private int calcPOC0(SliceHeader firstSliceHeader, NALUnit firstNu) {
if (firstNu.type == IDR_SLICE) {
prevPOCMsb = prevPOCLsb = 0;
}
int maxPOCLsbDiv2 = 1 << (firstSliceHeader.sps.log2_max_pic_order_cnt_lsb_minus4 + 3), maxPOCLsb = maxPOCLsbDiv2 << 1;
int POCLsb = firstSliceHeader.pic_order_cnt_lsb;
int POCMsb, POC;
if ((POCLsb < prevPOCLsb) && ((prevPOCLsb - POCLsb) >= maxPOCLsbDiv2))
POCMsb = prevPOCMsb + maxPOCLsb;
else if ((POCLsb > prevPOCLsb) && ((POCLsb - prevPOCLsb) > maxPOCLsbDiv2))
POCMsb = prevPOCMsb - maxPOCLsb;
else
POCMsb = prevPOCMsb;
POC = POCMsb + POCLsb;
if (firstNu.nal_ref_idc > 0) {
if (hasMMCO5(firstSliceHeader, firstNu)) {
prevPOCMsb = 0;
prevPOCLsb = POC;
} else {
prevPOCMsb = POCMsb;
prevPOCLsb = POCLsb;
}
}
return POC;
}
private boolean hasMMCO5(SliceHeader firstSliceHeader, NALUnit firstNu) {
if (firstNu.type != IDR_SLICE && firstSliceHeader.refPicMarkingNonIDR != null) {
Instruction[] instructions = firstSliceHeader.refPicMarkingNonIDR.getInstructions();
for (int i = 0; i < instructions.length; i++) {
Instruction instruction = instructions[i];
if (instruction.getType() == InstrType.CLEAR)
return true;
}
}
return false;
}
}