/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.frame.h264.type;
import java.nio.ByteBuffer;
import org.apache.log4j.Logger;
import com.ttProject.frame.h264.H264Frame;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
import com.ttProject.unit.extra.BitLoader;
import com.ttProject.unit.extra.bit.Bit1;
import com.ttProject.unit.extra.bit.Bit16;
import com.ttProject.unit.extra.bit.Bit2;
import com.ttProject.unit.extra.bit.Bit3;
import com.ttProject.unit.extra.bit.Bit32;
import com.ttProject.unit.extra.bit.Bit4;
import com.ttProject.unit.extra.bit.Bit5;
import com.ttProject.unit.extra.bit.Bit8;
import com.ttProject.unit.extra.bit.Seg;
import com.ttProject.unit.extra.bit.Ueg;
import com.ttProject.util.BufferUtil;
/**
* SequenceParameterSet
* hold profile, level and so on... something detail.
*
* 67 64 00 15 ac c8 60 20 09 6c 04 40 00 00 03 00 40 00 00 07 a3 c5 8b 67 80
*
* width = ((pic_width_in_mbs_minus1 +1)*16) - frame_crop_left_offset*2 - frame_crop_right_offset*2;
* height= ((2 - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2);
*
* ((31 + 1) * 16) - 0 * 2 - 0 * 2 = 512
* ((2 - 1) * (17 + 1) * 16) - 0 * 2 - 0 * 2 = 288
*
* 67 4D 40 1E 92 42 01 40 5F F2 E0 22 00 00 03 00 C8 00 00 2E D5 1E 2C 5C 90
*
* ok 640x380 is correct.
*
* 67 4D 40 1E D9 01 41 FA 10 00 00 03 00 10 00 00 7D 00 F1 62 E4 80
*
* @see http://stackoverflow.com/questions/6394874/fetching-the-dimensions-of-a-h264video-stream
*
* @author taktod
*/
public class SequenceParameterSet extends H264Frame {
/** logger */
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(SequenceParameterSet.class);
private Bit8 profileIdc = new Bit8();
private Bit1 constraintSet0Flag = new Bit1();
private Bit1 constraintSet1Flag = new Bit1();
private Bit1 constraintSet2Flag = new Bit1();
private Bit1 constraintSet3Flag = new Bit1();
private Bit1 constraintSet4Flag = new Bit1();
private Bit1 constraintSet5Flag = new Bit1();
private Bit2 reservedZeroBits = new Bit2();
private Bit8 levelIdc = new Bit8();
private Ueg seqParameterSetId = new Ueg();
private Ueg chromaFormatIdc = null;
private Bit1 separateColourPlaneFlag = null;
private Ueg bitDepthLumaMinus8 = null;
private Ueg bitDepthChromaMinus8 = null;
private Bit1 qpprimeYZeroTransformBypassFlag = null;
private Bit1 seqScalingMatrixPresentFlag = null;
@SuppressWarnings("unused")
private Bit1[] seqScalingListPresentFlag = null;
private Ueg log2MaxFrameNumMinus4 = new Ueg();
private Ueg picOrderCntType = new Ueg();
private Ueg log2MaxPicOrderCntLsbMinus4 = null;
private Bit1 deltaPicOrderAlwaysZeroFlag = null;
private Seg offsetForNonRefPic = null;
private Seg offsetForTopToBottomField = null;
private Ueg numRefFramesInPicOrderCntCycle = null;
private Seg[] offsetForRefFrame = null;
private Ueg maxNumRefFrames = new Ueg();
private Bit1 gapsInFrameNumValueAllowedFlag = new Bit1();
private Ueg picWidthInMbsMinus1 = new Ueg();
private Ueg picHeightInMapUnitsMinus1 = new Ueg();
private Bit1 frameMbsOnlyFlag = new Bit1();
private Bit1 mbAdaptiveFrameFieldFlag = null;
private Bit1 direct8x8InferenceFlag = new Bit1();
private Bit1 frameCroppingFlag = new Bit1();
private Ueg frameCropLeftOffset = null;
private Ueg frameCropRightOffset = null;
private Ueg frameCropTopOffset = null;
private Ueg frameCropBottomOffset = null;
private Bit1 vuiParametersPresentFlag = new Bit1();
private Bit1 aspectRatioInfoPresentFlag = null;
private Bit8 aspectRatioIdc = null;
private Bit16 sarWidth = null;
private Bit16 sarHeight = null;
private Bit1 overscanInfoPresentFlag = null;
private Bit1 overscanAppropriateFlag = null;
private Bit1 videoSignalTypePresentFlag = null;
private Bit3 videoFormat = null;
private Bit1 videoFullRangeFlag = null;
private Bit1 colourDescriptionPresentFlag = null;
private Bit8 colourPrimaries = null;
private Bit8 transferCharacteristics = null;
private Bit8 matrixCoefficients = null;
private Bit1 chromaLocInfoPresentFlag = null;
private Ueg chromaSampleLocTypeTopField = null;
private Ueg chromaSampleLocTypeBottomField = null;
private Bit1 timingInfoPresentFlag = null;
private Bit32 numUnitsInTick = null;
private Bit32 timeScale = null;
private Bit1 fixedFrameRateFlag = null;
private Bit1 nalHrdParametersPresentFlag = null;
private Bit1 vclHrdParametersPresentFlag = null;
// hrdParameters
private Ueg cpbCntMinus1 = null;
private Bit4 bitRateScale = null;
private Bit4 cpbSizeScale = null;
private Ueg[] bitRateValueMinus1 = null;
private Ueg[] cpbSizeValueMinus1 = null;
private Bit1[] cbrFlag = null;
private Bit5 initialCpbRemovalDelayLengthMinus1 = null;
private Bit5 cpbRemovalDelayLengthMinus1 = null;
private Bit5 dpbOutputDelayLengthMinus1 = null;
private Bit5 timeOffsetLength = null;
private Bit1 lowDelayHrdFlag = null;
private Bit1 picStructPresentFlag = null;
private Bit1 bitstreamRestrictionFlag = null;
private Bit1 motionVectorsOverPicBoundariesFlag = null;
private Ueg maxBytesPerPicDenom = null;
private Ueg maxBitsPerMbDenom = null;
private Ueg log2MaxMvLengthHorizontal = null;
private Ueg log2MaxMvLengthVertical = null;
private Ueg maxNumReorderFrames = null;
private Ueg maxDecFrameBuffering = null;
private ByteBuffer buffer = null;
/**
* constructor
* @param forbiddenZeroBit
* @param nalRefIdc
* @param type
*/
public SequenceParameterSet(Bit1 forbiddenZeroBit,
Bit2 nalRefIdc,
Bit5 type) {
super(forbiddenZeroBit, nalRefIdc, type);
}
/**
* {@inheritDoc}
*/
@Override
public void minimumLoad(IReadChannel channel) throws Exception {
// have the copy of buffer.
buffer = BufferUtil.safeRead(channel, channel.size() - channel.position());
BitLoader loader = new BitLoader(new ByteReadChannel(buffer.duplicate()));
loader.setEmulationPreventionFlg(true);
loader.load(profileIdc,
constraintSet0Flag,
constraintSet1Flag,
constraintSet2Flag,
constraintSet3Flag,
constraintSet4Flag,
constraintSet5Flag,
reservedZeroBits,
levelIdc,
seqParameterSetId);
int profile = profileIdc.get();
if(profile == 100 || profile == 110 ||
profile == 122 || profile == 244 || profile == 44 ||
profile == 83 || profile == 86 || profile == 118 ||
profile == 128 || profile == 138) {
chromaFormatIdc = new Ueg();
loader.load(chromaFormatIdc);
if(chromaFormatIdc.get() == 3) {
separateColourPlaneFlag = new Bit1();
loader.load(separateColourPlaneFlag);
}
bitDepthLumaMinus8 = new Ueg();
bitDepthChromaMinus8 = new Ueg();
qpprimeYZeroTransformBypassFlag = new Bit1();
seqScalingMatrixPresentFlag = new Bit1();
loader.load(bitDepthLumaMinus8,
bitDepthChromaMinus8,
qpprimeYZeroTransformBypassFlag,
seqScalingMatrixPresentFlag);
if(seqScalingMatrixPresentFlag.get() == 1) {
throw new Exception("seqScalingMatrix is unexpected");
}
}
loader.load(log2MaxFrameNumMinus4,
picOrderCntType);
if(picOrderCntType.get() == 0) {
log2MaxPicOrderCntLsbMinus4 = new Ueg();
loader.load(log2MaxPicOrderCntLsbMinus4);
}
else if(picOrderCntType.get() == 1){
deltaPicOrderAlwaysZeroFlag = new Bit1();
offsetForNonRefPic = new Seg();
offsetForTopToBottomField = new Seg();
numRefFramesInPicOrderCntCycle = new Ueg();
loader.load(deltaPicOrderAlwaysZeroFlag,
offsetForNonRefPic,
offsetForTopToBottomField,
numRefFramesInPicOrderCntCycle);
int cnt = numRefFramesInPicOrderCntCycle.get();
offsetForRefFrame = new Seg[cnt];
for(int i = 0;i < cnt;i ++) {
offsetForRefFrame[i] = new Seg();
loader.load(offsetForRefFrame[i]);
}
}
loader.load(maxNumRefFrames,
gapsInFrameNumValueAllowedFlag,
picWidthInMbsMinus1,
picHeightInMapUnitsMinus1,
frameMbsOnlyFlag);
if(frameMbsOnlyFlag.get() == 0) {
mbAdaptiveFrameFieldFlag = new Bit1();
loader.load(mbAdaptiveFrameFieldFlag);
}
loader.load(direct8x8InferenceFlag,
frameCroppingFlag);
if(frameCroppingFlag.get() == 1) {
frameCropLeftOffset = new Ueg();
frameCropRightOffset = new Ueg();
frameCropTopOffset = new Ueg();
frameCropBottomOffset = new Ueg();
loader.load(frameCropLeftOffset,
frameCropRightOffset,
frameCropTopOffset,
frameCropBottomOffset);
}
loader.load(vuiParametersPresentFlag);
if(vuiParametersPresentFlag.get() == 1) {
// load vuiParameters
loadVuiParameters(loader);
}
// now we can know the width x height
super.setReadPosition(channel.position());
super.setSize(channel.size());
int width = (picWidthInMbsMinus1.get() + 1) * 16;
int height = ((2 - frameMbsOnlyFlag.get()) * (picHeightInMapUnitsMinus1.get() + 1) * 16);
if(frameCroppingFlag.get() == 1) {
width = width - frameCropLeftOffset.get() * 2 - frameCropRightOffset.get() * 2;
height = height - frameCropTopOffset.get() * 2 - frameCropBottomOffset.get() * 2;
}
super.setWidth(width);
super.setHeight(height);
super.update();
}
/**
* load vuiParameter
*/
private void loadVuiParameters(BitLoader loader) throws Exception {
aspectRatioInfoPresentFlag = new Bit1();
loader.load(aspectRatioInfoPresentFlag);
if(aspectRatioInfoPresentFlag.get() != 0) {
aspectRatioIdc = new Bit8();
loader.load(aspectRatioIdc);
if(aspectRatioIdc.get() == 255) {
sarWidth = new Bit16();
sarHeight = new Bit16();
loader.load(sarWidth, sarHeight);
}
}
overscanInfoPresentFlag = new Bit1();
loader.load(overscanInfoPresentFlag);
if(overscanInfoPresentFlag.get() == 1) {
overscanAppropriateFlag = new Bit1();
loader.load(overscanAppropriateFlag);
}
videoSignalTypePresentFlag = new Bit1();
loader.load(videoSignalTypePresentFlag);
if(videoSignalTypePresentFlag.get() == 1) {
videoFormat = new Bit3();
videoFullRangeFlag = new Bit1();
colourDescriptionPresentFlag = new Bit1();
loader.load(videoFormat, videoFullRangeFlag, colourDescriptionPresentFlag);
if(colourDescriptionPresentFlag.get() == 1) {
colourPrimaries = new Bit8();
transferCharacteristics = new Bit8();
matrixCoefficients = new Bit8();
loader.load(colourPrimaries, transferCharacteristics, matrixCoefficients);
}
}
chromaLocInfoPresentFlag = new Bit1();
loader.load(chromaLocInfoPresentFlag);
if(chromaLocInfoPresentFlag.get() == 1) {
chromaSampleLocTypeTopField = new Ueg();
chromaSampleLocTypeBottomField = new Ueg();
loader.load(chromaSampleLocTypeTopField, chromaSampleLocTypeBottomField);
}
timingInfoPresentFlag = new Bit1();
loader.load(timingInfoPresentFlag);
if(timingInfoPresentFlag.get() == 1) {
numUnitsInTick = new Bit32();
timeScale = new Bit32();
fixedFrameRateFlag = new Bit1();
loader.load(numUnitsInTick, timeScale, fixedFrameRateFlag);
}
nalHrdParametersPresentFlag = new Bit1();
loader.load(nalHrdParametersPresentFlag);
if(nalHrdParametersPresentFlag.get() == 1) {
loadHrdParameters(loader);
}
vclHrdParametersPresentFlag = new Bit1();
loader.load(vclHrdParametersPresentFlag);
if(vclHrdParametersPresentFlag.get() == 1) {
loadHrdParameters(loader);
}
if(nalHrdParametersPresentFlag.get() == 1 || vclHrdParametersPresentFlag.get() == 1) {
lowDelayHrdFlag = new Bit1();
loader.load(lowDelayHrdFlag);
}
picStructPresentFlag = new Bit1();
bitstreamRestrictionFlag = new Bit1();
loader.load(picStructPresentFlag, bitstreamRestrictionFlag);
if(bitstreamRestrictionFlag.get() == 1) {
motionVectorsOverPicBoundariesFlag = new Bit1();
maxBytesPerPicDenom = new Ueg();
maxBitsPerMbDenom = new Ueg();
log2MaxMvLengthHorizontal = new Ueg();
log2MaxMvLengthVertical = new Ueg();
maxNumReorderFrames = new Ueg();
maxDecFrameBuffering = new Ueg();
loader.load(motionVectorsOverPicBoundariesFlag, maxBytesPerPicDenom, maxBitsPerMbDenom,
log2MaxMvLengthHorizontal, log2MaxMvLengthVertical, maxNumReorderFrames, maxDecFrameBuffering);
}
}
/**
* load hrdParameters
* can be loaded twice, however, both can have the same data.
* if different, throw the exception.
* @param loader
*/
private void loadHrdParameters(BitLoader loader) throws Exception {
Ueg val1 = new Ueg();
Bit4 val2 = new Bit4();
Bit4 val3 = new Bit4();
// cpbCntMinus1 = new Ueg();
// bitRateScale = new Bit4();
// cpbSizeScale = new Bit4();
loader.load(val1, val2, val3);
if(cpbCntMinus1 == null) {
cpbCntMinus1 = val1;
}
else if(cpbCntMinus1.get() != val1.get()) {
throw new RuntimeException("cpbCntMinus1 is different from previous one");
}
if(bitRateScale == null) {
bitRateScale = val2;
}
else if(bitRateScale.get() != val2.get()) {
throw new RuntimeException("bitRateScale is different from previous one");
}
if(cpbSizeScale == null) {
cpbSizeScale = val3;
}
else if(cpbSizeScale.get() != val3.get()) {
throw new RuntimeException("cpbSizeScale is different from previous one");
}
int num = cpbCntMinus1.get() + 1;
if(bitRateValueMinus1 == null) {
bitRateValueMinus1 = new Ueg[num];
}
if(cpbSizeValueMinus1 == null) {
cpbSizeValueMinus1 = new Ueg[num];
}
if(cbrFlag == null) {
cbrFlag = new Bit1[num];
}
for(int schedSelIdx = 0;schedSelIdx < num;schedSelIdx ++) {
Ueg val4 = new Ueg();
Ueg val5 = new Ueg();
Bit1 val6 = new Bit1();
loader.load(val4, val5, val6);
if(bitRateValueMinus1[schedSelIdx] == null) {
bitRateValueMinus1[schedSelIdx] = val4;
}
else if(bitRateValueMinus1[schedSelIdx].get() != val4.get()) {
throw new RuntimeException("bitRateValueMinus1 is different from previous one");
}
if(cpbSizeValueMinus1[schedSelIdx] == null) {
cpbSizeValueMinus1[schedSelIdx] = val5;
}
else if(cpbSizeValueMinus1[schedSelIdx].get() != val5.get()) {
throw new RuntimeException("cpbSizeValueMinus1 is different from previous one");
}
if(cbrFlag[schedSelIdx] == null) {
cbrFlag[schedSelIdx] = val6;
}
else if(cbrFlag[schedSelIdx].get() != val6.get()) {
throw new RuntimeException("cbrFlag is different from previous one");
}
bitRateValueMinus1[schedSelIdx] = val4;
cpbSizeValueMinus1[schedSelIdx] = val5;
cbrFlag[schedSelIdx] = val6;
}
Bit5 val7 = new Bit5();
Bit5 val8 = new Bit5();
Bit5 val9 = new Bit5();
Bit5 val10 = new Bit5();
loader.load(val7, val8, val9, val10);
if(initialCpbRemovalDelayLengthMinus1 == null) {
initialCpbRemovalDelayLengthMinus1 = val7;
}
else if(initialCpbRemovalDelayLengthMinus1.get() != val7.get()) {
throw new RuntimeException("initialCpbRemovalDelayLengthMinus1 is different from previous one");
}
if(cpbRemovalDelayLengthMinus1 == null) {
cpbRemovalDelayLengthMinus1 = val8;
}
else if(cpbRemovalDelayLengthMinus1.get() != val8.get()) {
throw new RuntimeException("cpbRemovalDelayLengthMinus1 is different from previous one");
}
if(dpbOutputDelayLengthMinus1 == null) {
dpbOutputDelayLengthMinus1 = val9;
}
else if(dpbOutputDelayLengthMinus1.get() != val9.get()) {
throw new RuntimeException("dpbOutputDelayLengthMinus1 is different from previous one");
}
if(timeOffsetLength == null) {
timeOffsetLength = val10;
}
else if(timeOffsetLength.get() != val10.get()) {
throw new RuntimeException("timeOffsetLength is different from previous one");
}
}
/**
* {@inheritDoc}
*/
@Override
public void load(IReadChannel channel) throws Exception {
// channel.position(super.getReadPosition());
// buffer = BufferUtil.safeRead(channel, getSize() - getReadPosition());
super.update();
}
/**
* {@inheritDoc}
*/
@Override
protected void requestUpdate() throws Exception {
if(buffer == null) {
throw new Exception("body data is undefined.");
}
// TODO この部分のこの方法は修正しないとだめ、自力でコネクトしてやるのではなく、大元のデータを保持しておいて、それを応答するようにする。
// でないと、00 00 03の扱いを自力で作る必要がでてくるので、非常にややこしいことになる。
// ここの結合動作をつくっておく必要がありそう。(でも読み込んだデータをベースにしておいた方がいいかも・・・)
/* BitConnector connector = new BitConnector();
// コネクトしていく。
connector.feed(profileIdc, constraintSet0Flag, constraintSet1Flag,
constraintSet2Flag, constraintSet3Flag, constraintSet4Flag,
constraintSet5Flag, reservedZeroBits, levelIdc, seqParameterSetId,
chromaFormatIdc, separateColourPlaneFlag, bitDepthLumaMinus8,
bitDepthChromaMinus8, qpprimeYZeroTransformBypassFlag,
seqScalingMatrixPresentFlag);
connector.feed(log2MaxFrameNumMinus4, picOrderCntType, log2MaxPicOrderCntLsbMinus4,
deltaPicOrderAlwaysZeroFlag, offsetForNonRefPic, offsetForTopToBottomField,
numRefFramesInPicOrderCntCycle);
if(numRefFramesInPicOrderCntCycle != null && numRefFramesInPicOrderCntCycle.get() != 0) {
for(int i = 0;i < numRefFramesInPicOrderCntCycle.get();i ++) {
connector.feed(offsetForRefFrame[i]);
}
}
connector.feed(maxNumRefFrames, gapsInFrameNumValueAllowedFlag,
picWidthInMbsMinus1, picHeightInMapUnitsMinus1, frameMbsOnlyFlag,
mbAdaptiveFrameFieldFlag, direct8x8InferenceFlag, frameCroppingFlag,
frameCropLeftOffset, frameCropRightOffset, frameCropTopOffset, frameCropBottomOffset,
vuiParametersPresentFlag, extraBit);
setData(BufferUtil.connect(getTypeBuffer(),
connector.connect(),
buffer));*/
setData(BufferUtil.connect(getTypeBuffer(), buffer));
}
/**
* {@inheritDoc}
*/
@Override
public ByteBuffer getPackBuffer() {
return null;
}
/**
*
* @return
*/
public boolean getNalHrdBpPresentFlag() {
if(nalHrdParametersPresentFlag != null && nalHrdParametersPresentFlag.get() == 1) {
return true;
}
return false;
}
/**
*
* @return
*/
public boolean getVclHrdBpPresentFlag() {
if(vclHrdParametersPresentFlag != null && vclHrdParametersPresentFlag.get() == 1) {
return true;
}
return false;
}
/**
*
* @return
*/
public boolean getCpbDpbDelaysPresentFlag() {
if(getNalHrdBpPresentFlag()) {
return true;
}
if(getVclHrdBpPresentFlag()) {
return true;
}
return false;
}
/**
* cpbCntMinus1の値を参照する
* @return
*/
public int getCpbCntMinus1() {
if(cpbCntMinus1 == null) {
throw new RuntimeException("cpbCntMinus1 is undefined, however, try to ref.");
}
return cpbCntMinus1.get();
}
public int getInitialCpbRemovalDelayLengthMinus1() {
if(initialCpbRemovalDelayLengthMinus1 == null) {
throw new RuntimeException("initialCpbRemovalDelayLengthMinus1 is undefined, however, try to ref.");
}
return initialCpbRemovalDelayLengthMinus1.get();
}
public int getPicStructPresentFlag() {
if(picStructPresentFlag == null) {
throw new RuntimeException("picStructPresentFlag is undefined, however, try to ref.");
}
return picStructPresentFlag.get();
}
public int getTimeOffsetLength() {
if(timeOffsetLength == null) {
throw new RuntimeException("timeoffsetLength is undefined, however, try to ref.");
}
return timeOffsetLength.get();
}
}