/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.media.h264.frame; import org.apache.log4j.Logger; import com.ttProject.media.extra.Bit1; import com.ttProject.media.extra.Bit2; import com.ttProject.media.extra.Bit8; import com.ttProject.media.extra.BitLoader; import com.ttProject.media.extra.Seg; import com.ttProject.media.extra.Ueg; import com.ttProject.media.h264.Frame; import com.ttProject.media.h264.IFrameAnalyzer; import com.ttProject.nio.channels.ByteReadChannel; import com.ttProject.nio.channels.IReadChannel; /** * SequenceParameterSet * profileとかlevelとか、その他の細かい設定とかがはいっているみたい。 * * 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 * * あってますね。640x380とれました。 * * @see http://stackoverflow.com/questions/6394874/fetching-the-dimensions-of-a-h264video-stream * * @author taktod */ public class SequenceParameterSet extends Frame { /** ロガー */ @SuppressWarnings("unused") private Logger logger = Logger.getLogger(SequenceParameterSet.class); // 先頭の3バイトからこのデータが取得可能 private Bit8 profileIdc; private Bit1 constraintSet0Flag; private Bit1 constraintSet1Flag; private Bit1 constraintSet2Flag; private Bit1 constraintSet3Flag; private Bit1 constraintSet4Flag; private Bit1 constraintSet5Flag; private Bit2 reservedZeroBits; private Bit8 levelIdc; private Ueg seqParameterSetId; private Ueg chromaFormatIdc; private Bit1 separateColourPlaneFlag; private Ueg bitDepthLumaMinus8; private Ueg bitDepthChromaMinus8; private Bit1 qpprimeYZeroTransformBypassFlag; private Bit1 seqScalingMatrixPresentFlag; @SuppressWarnings("unused") private Bit1[] seqScalingListPresentFlag; private Ueg log2MaxFrameNumMinus4; private Ueg picOrderCntType; private Ueg log2MaxPicOrderCntLsbMinus4; private Bit1 deltaPicOrderAlwaysZeroFlag; private Seg offsetForNonRefPic; private Seg offsetForTopToBottomField; private Ueg numRefFramesInPicOrderCntCycle; private Seg[] offsetForRefFrame; private Ueg maxNumRefFrames; private Bit1 gapsInFrameNumValueAllowedFlag; private Ueg picWidthInMbsMinus1; private Ueg picHeightInMapUnitsMinus1; private Bit1 frameMbsOnlyFlag; private Bit1 mbAdaptiveFrameFieldFlag; private Bit1 direct8x8InferenceFlag; private Bit1 frameCroppingFlag; private Ueg frameCropLeftOffset; private Ueg frameCropRightOffset; private Ueg frameCropTopOffset; private Ueg frameCropBottomOffset; private Bit1 vuiParametersPresentFlag; private int width = -1; private int height = -1; public SequenceParameterSet(int size, byte frameTypeData) { super(size, frameTypeData); } public SequenceParameterSet(byte frameTypeData) { this(0, frameTypeData); } @Override public void analyze(IReadChannel ch, IFrameAnalyzer analyzer) throws Exception { super.analyze(ch, analyzer); BitLoader bitLoader = new BitLoader(new ByteReadChannel(getBuffer())); profileIdc = new Bit8(); constraintSet0Flag = new Bit1(); constraintSet1Flag = new Bit1(); constraintSet2Flag = new Bit1(); constraintSet3Flag = new Bit1(); constraintSet4Flag = new Bit1(); constraintSet5Flag = new Bit1(); reservedZeroBits = new Bit2(); levelIdc = new Bit8(); seqParameterSetId = new Ueg(); bitLoader.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(); bitLoader.load(chromaFormatIdc); if(chromaFormatIdc.getData() == 3) { separateColourPlaneFlag = new Bit1(); bitLoader.load(separateColourPlaneFlag); } bitDepthLumaMinus8 = new Ueg(); bitDepthChromaMinus8 = new Ueg(); qpprimeYZeroTransformBypassFlag = new Bit1(); seqScalingMatrixPresentFlag = new Bit1(); bitLoader.load(bitDepthLumaMinus8, bitDepthChromaMinus8, qpprimeYZeroTransformBypassFlag, seqScalingMatrixPresentFlag); if(seqScalingMatrixPresentFlag.get() == 1) { throw new Exception("seqScalingMatrixの解析動作は未実装です。"); } } log2MaxFrameNumMinus4 = new Ueg(); picOrderCntType = new Ueg(); bitLoader.load(log2MaxFrameNumMinus4, picOrderCntType); if(picOrderCntType.getData() == 0) { log2MaxPicOrderCntLsbMinus4 = new Ueg(); bitLoader.load(log2MaxPicOrderCntLsbMinus4); } else if(picOrderCntType.getData() == 1) { deltaPicOrderAlwaysZeroFlag = new Bit1(); offsetForNonRefPic = new Seg(); offsetForTopToBottomField = new Seg(); numRefFramesInPicOrderCntCycle = new Ueg(); bitLoader.load(deltaPicOrderAlwaysZeroFlag, offsetForNonRefPic, offsetForTopToBottomField, numRefFramesInPicOrderCntCycle); int cnt = numRefFramesInPicOrderCntCycle.getData(); offsetForRefFrame = new Seg[cnt]; for(int i = 0;i < cnt;i ++) { offsetForRefFrame[i] = new Seg(); bitLoader.load(offsetForRefFrame[i]); } } maxNumRefFrames = new Ueg(); gapsInFrameNumValueAllowedFlag = new Bit1(); picWidthInMbsMinus1 = new Ueg(); picHeightInMapUnitsMinus1 = new Ueg(); frameMbsOnlyFlag = new Bit1(); bitLoader.load(maxNumRefFrames, gapsInFrameNumValueAllowedFlag, picWidthInMbsMinus1, picHeightInMapUnitsMinus1, frameMbsOnlyFlag); if(frameMbsOnlyFlag.get() == 0) { mbAdaptiveFrameFieldFlag = new Bit1(); bitLoader.load(mbAdaptiveFrameFieldFlag); } direct8x8InferenceFlag = new Bit1(); frameCroppingFlag = new Bit1(); bitLoader.load(direct8x8InferenceFlag, frameCroppingFlag); if(frameCroppingFlag.get() == 1) { frameCropLeftOffset = new Ueg(); frameCropRightOffset = new Ueg(); frameCropTopOffset = new Ueg(); frameCropBottomOffset = new Ueg(); bitLoader.load(frameCropLeftOffset, frameCropRightOffset, frameCropTopOffset, frameCropBottomOffset); } vuiParametersPresentFlag = new Bit1(); bitLoader.load(vuiParametersPresentFlag); if(vuiParametersPresentFlag.get() == 1) { // parameterを読み込む } width = (picWidthInMbsMinus1.getData() + 1) * 16; height = ((2 - frameMbsOnlyFlag.get()) * (picHeightInMapUnitsMinus1.getData() + 1) * 16); if(frameCroppingFlag.get() == 1) { width = width - frameCropLeftOffset.getData() * 2 - frameCropRightOffset.getData() * 2; height = height - frameCropTopOffset.getData() * 2 - frameCropBottomOffset.getData() * 2; } } @Override public int getHeight() { return height; } @Override public int getWidth() { return width; } }