package org.mp4parser.boxes.iso14496.part15; import org.mp4parser.boxes.iso14496.part1.objectdescriptors.BitReaderBuffer; import org.mp4parser.boxes.iso14496.part1.objectdescriptors.BitWriterBuffer; import org.mp4parser.tools.Hex; import org.mp4parser.tools.IsoTypeReader; import org.mp4parser.tools.IsoTypeWriter; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; /** * */ public class AvcDecoderConfigurationRecord { public int configurationVersion; public int avcProfileIndication; public int profileCompatibility; public int avcLevelIndication; public int lengthSizeMinusOne; public List<ByteBuffer> sequenceParameterSets = new ArrayList<ByteBuffer>(); public List<ByteBuffer> pictureParameterSets = new ArrayList<ByteBuffer>(); public boolean hasExts = true; public int chromaFormat = 1; public int bitDepthLumaMinus8 = 0; public int bitDepthChromaMinus8 = 0; public List<ByteBuffer> sequenceParameterSetExts = new ArrayList<ByteBuffer>(); /** * Just for non-spec-conform encoders */ public int lengthSizeMinusOnePaddingBits = 63; public int numberOfSequenceParameterSetsPaddingBits = 7; public int chromaFormatPaddingBits = 31; public int bitDepthLumaMinus8PaddingBits = 31; public int bitDepthChromaMinus8PaddingBits = 31; public AvcDecoderConfigurationRecord() { } public AvcDecoderConfigurationRecord(ByteBuffer content) { configurationVersion = IsoTypeReader.readUInt8(content); avcProfileIndication = IsoTypeReader.readUInt8(content); profileCompatibility = IsoTypeReader.readUInt8(content); avcLevelIndication = IsoTypeReader.readUInt8(content); BitReaderBuffer brb = new BitReaderBuffer(content); lengthSizeMinusOnePaddingBits = brb.readBits(6); lengthSizeMinusOne = brb.readBits(2); numberOfSequenceParameterSetsPaddingBits = brb.readBits(3); int numberOfSeuqenceParameterSets = brb.readBits(5); for (int i = 0; i < numberOfSeuqenceParameterSets; i++) { int sequenceParameterSetLength = IsoTypeReader.readUInt16(content); byte[] sequenceParameterSetNALUnit = new byte[sequenceParameterSetLength]; content.get(sequenceParameterSetNALUnit); sequenceParameterSets.add(ByteBuffer.wrap(sequenceParameterSetNALUnit)); } long numberOfPictureParameterSets = IsoTypeReader.readUInt8(content); for (int i = 0; i < numberOfPictureParameterSets; i++) { int pictureParameterSetLength = IsoTypeReader.readUInt16(content); byte[] pictureParameterSetNALUnit = new byte[pictureParameterSetLength]; content.get(pictureParameterSetNALUnit); pictureParameterSets.add(ByteBuffer.wrap(pictureParameterSetNALUnit)); } if (content.remaining() < 4) { hasExts = false; } if (hasExts && (avcProfileIndication == 100 || avcProfileIndication == 110 || avcProfileIndication == 122 || avcProfileIndication == 144)) { // actually only some bits are interesting so masking with & x would be good but not all Mp4 creating tools set the reserved bits to 1. // So we need to store all bits brb = new BitReaderBuffer(content); chromaFormatPaddingBits = brb.readBits(6); chromaFormat = brb.readBits(2); bitDepthLumaMinus8PaddingBits = brb.readBits(5); bitDepthLumaMinus8 = brb.readBits(3); bitDepthChromaMinus8PaddingBits = brb.readBits(5); bitDepthChromaMinus8 = brb.readBits(3); long numOfSequenceParameterSetExt = IsoTypeReader.readUInt8(content); for (int i = 0; i < numOfSequenceParameterSetExt; i++) { int sequenceParameterSetExtLength = IsoTypeReader.readUInt16(content); byte[] sequenceParameterSetExtNALUnit = new byte[sequenceParameterSetExtLength]; content.get(sequenceParameterSetExtNALUnit); sequenceParameterSetExts.add(ByteBuffer.wrap(sequenceParameterSetExtNALUnit)); } } else { chromaFormat = -1; bitDepthLumaMinus8 = -1; bitDepthChromaMinus8 = -1; } } public void getContent(ByteBuffer byteBuffer) { IsoTypeWriter.writeUInt8(byteBuffer, configurationVersion); IsoTypeWriter.writeUInt8(byteBuffer, avcProfileIndication); IsoTypeWriter.writeUInt8(byteBuffer, profileCompatibility); IsoTypeWriter.writeUInt8(byteBuffer, avcLevelIndication); BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer); bwb.writeBits(lengthSizeMinusOnePaddingBits, 6); bwb.writeBits(lengthSizeMinusOne, 2); bwb.writeBits(numberOfSequenceParameterSetsPaddingBits, 3); bwb.writeBits(pictureParameterSets.size(), 5); for (ByteBuffer sequenceParameterSetNALUnit : sequenceParameterSets) { IsoTypeWriter.writeUInt16(byteBuffer, sequenceParameterSetNALUnit.limit()); byteBuffer.put((ByteBuffer) sequenceParameterSetNALUnit.rewind()); } IsoTypeWriter.writeUInt8(byteBuffer, pictureParameterSets.size()); for (ByteBuffer pictureParameterSetNALUnit : pictureParameterSets) { IsoTypeWriter.writeUInt16(byteBuffer, pictureParameterSetNALUnit.limit()); byteBuffer.put((ByteBuffer) pictureParameterSetNALUnit.rewind()); } if (hasExts && (avcProfileIndication == 100 || avcProfileIndication == 110 || avcProfileIndication == 122 || avcProfileIndication == 144)) { bwb = new BitWriterBuffer(byteBuffer); bwb.writeBits(chromaFormatPaddingBits, 6); bwb.writeBits(chromaFormat, 2); bwb.writeBits(bitDepthLumaMinus8PaddingBits, 5); bwb.writeBits(bitDepthLumaMinus8, 3); bwb.writeBits(bitDepthChromaMinus8PaddingBits, 5); bwb.writeBits(bitDepthChromaMinus8, 3); for (ByteBuffer sequenceParameterSetExtNALUnit : sequenceParameterSetExts) { IsoTypeWriter.writeUInt16(byteBuffer, sequenceParameterSetExtNALUnit.limit()); byteBuffer.put((ByteBuffer) sequenceParameterSetExtNALUnit.reset()); } } } public long getContentSize() { long size = 5; size += 1; // sequenceParamsetLength for (ByteBuffer sequenceParameterSetNALUnit : sequenceParameterSets) { size += 2; //lengthSizeMinusOne field size += sequenceParameterSetNALUnit.limit(); } size += 1; // pictureParamsetLength for (ByteBuffer pictureParameterSetNALUnit : pictureParameterSets) { size += 2; //lengthSizeMinusOne field size += pictureParameterSetNALUnit.limit(); } if (hasExts && (avcProfileIndication == 100 || avcProfileIndication == 110 || avcProfileIndication == 122 || avcProfileIndication == 144)) { size += 4; for (ByteBuffer sequenceParameterSetExtNALUnit : sequenceParameterSetExts) { size += 2; size += sequenceParameterSetExtNALUnit.limit(); } } return size; } public List<String> getSequenceParameterSetsAsStrings() { List<String> result = new ArrayList<String>(sequenceParameterSets.size()); for (ByteBuffer parameterSet : sequenceParameterSets) { result.add(Hex.encodeHex(parameterSet)); } return result; } public List<String> getSequenceParameterSetExtsAsStrings() { List<String> result = new ArrayList<String>(sequenceParameterSetExts.size()); for (ByteBuffer parameterSet : sequenceParameterSetExts) { result.add(Hex.encodeHex(parameterSet)); } return result; } public List<String> getPictureParameterSetsAsStrings() { List<String> result = new ArrayList<String>(pictureParameterSets.size()); for (ByteBuffer parameterSet : pictureParameterSets) { result.add(Hex.encodeHex(parameterSet)); } return result; } @Override public String toString() { return "AvcDecoderConfigurationRecord{" + "configurationVersion=" + configurationVersion + ", avcProfileIndication=" + avcProfileIndication + ", profileCompatibility=" + profileCompatibility + ", avcLevelIndication=" + avcLevelIndication + ", lengthSizeMinusOne=" + lengthSizeMinusOne + ", hasExts=" + hasExts + ", chromaFormat=" + chromaFormat + ", bitDepthLumaMinus8=" + bitDepthLumaMinus8 + ", bitDepthChromaMinus8=" + bitDepthChromaMinus8 + ", lengthSizeMinusOnePaddingBits=" + lengthSizeMinusOnePaddingBits + ", numberOfSequenceParameterSetsPaddingBits=" + numberOfSequenceParameterSetsPaddingBits + ", chromaFormatPaddingBits=" + chromaFormatPaddingBits + ", bitDepthLumaMinus8PaddingBits=" + bitDepthLumaMinus8PaddingBits + ", bitDepthChromaMinus8PaddingBits=" + bitDepthChromaMinus8PaddingBits + '}'; } }