diff options
Diffstat (limited to 'isoparser/src/main/java/com/coremedia/iso/boxes/h264/AvcConfigurationBox.java')
-rw-r--r-- | isoparser/src/main/java/com/coremedia/iso/boxes/h264/AvcConfigurationBox.java | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/isoparser/src/main/java/com/coremedia/iso/boxes/h264/AvcConfigurationBox.java b/isoparser/src/main/java/com/coremedia/iso/boxes/h264/AvcConfigurationBox.java new file mode 100644 index 0000000..52f3695 --- /dev/null +++ b/isoparser/src/main/java/com/coremedia/iso/boxes/h264/AvcConfigurationBox.java @@ -0,0 +1,378 @@ +/* + * Copyright 2008 CoreMedia AG, Hamburg + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.coremedia.iso.boxes.h264; + +import com.coremedia.iso.Hex; +import com.coremedia.iso.IsoTypeReader; +import com.coremedia.iso.IsoTypeWriter; +import com.googlecode.mp4parser.AbstractBox; +import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer; +import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer; +import com.googlecode.mp4parser.h264.model.PictureParameterSet; +import com.googlecode.mp4parser.h264.model.SeqParameterSet; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Defined in ISO/IEC 14496-15:2004. + */ +public final class AvcConfigurationBox extends AbstractBox { + public static final String TYPE = "avcC"; + + public AVCDecoderConfigurationRecord avcDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); + + + public AvcConfigurationBox() { + super(TYPE); + } + + public int getConfigurationVersion() { + return avcDecoderConfigurationRecord.configurationVersion; + } + + public int getAvcProfileIndication() { + return avcDecoderConfigurationRecord.avcProfileIndication; + } + + public int getProfileCompatibility() { + return avcDecoderConfigurationRecord.profileCompatibility; + } + + public int getAvcLevelIndication() { + return avcDecoderConfigurationRecord.avcLevelIndication; + } + + public int getLengthSizeMinusOne() { + return avcDecoderConfigurationRecord.lengthSizeMinusOne; + } + + public List<byte[]> getSequenceParameterSets() { + return Collections.unmodifiableList(avcDecoderConfigurationRecord.sequenceParameterSets); + } + + public List<byte[]> getPictureParameterSets() { + return Collections.unmodifiableList(avcDecoderConfigurationRecord.pictureParameterSets); + } + + public void setConfigurationVersion(int configurationVersion) { + this.avcDecoderConfigurationRecord.configurationVersion = configurationVersion; + } + + public void setAvcProfileIndication(int avcProfileIndication) { + this.avcDecoderConfigurationRecord.avcProfileIndication = avcProfileIndication; + } + + public void setProfileCompatibility(int profileCompatibility) { + this.avcDecoderConfigurationRecord.profileCompatibility = profileCompatibility; + } + + public void setAvcLevelIndication(int avcLevelIndication) { + this.avcDecoderConfigurationRecord.avcLevelIndication = avcLevelIndication; + } + + public void setLengthSizeMinusOne(int lengthSizeMinusOne) { + this.avcDecoderConfigurationRecord.lengthSizeMinusOne = lengthSizeMinusOne; + } + + public void setSequenceParameterSets(List<byte[]> sequenceParameterSets) { + this.avcDecoderConfigurationRecord.sequenceParameterSets = sequenceParameterSets; + } + + public void setPictureParameterSets(List<byte[]> pictureParameterSets) { + this.avcDecoderConfigurationRecord.pictureParameterSets = pictureParameterSets; + } + + public int getChromaFormat() { + return avcDecoderConfigurationRecord.chromaFormat; + } + + public void setChromaFormat(int chromaFormat) { + this.avcDecoderConfigurationRecord.chromaFormat = chromaFormat; + } + + public int getBitDepthLumaMinus8() { + return avcDecoderConfigurationRecord.bitDepthLumaMinus8; + } + + public void setBitDepthLumaMinus8(int bitDepthLumaMinus8) { + this.avcDecoderConfigurationRecord.bitDepthLumaMinus8 = bitDepthLumaMinus8; + } + + public int getBitDepthChromaMinus8() { + return avcDecoderConfigurationRecord.bitDepthChromaMinus8; + } + + public void setBitDepthChromaMinus8(int bitDepthChromaMinus8) { + this.avcDecoderConfigurationRecord.bitDepthChromaMinus8 = bitDepthChromaMinus8; + } + + public List<byte[]> getSequenceParameterSetExts() { + return avcDecoderConfigurationRecord.sequenceParameterSetExts; + } + + public void setSequenceParameterSetExts(List<byte[]> sequenceParameterSetExts) { + this.avcDecoderConfigurationRecord.sequenceParameterSetExts = sequenceParameterSetExts; + } + + public boolean hasExts() { + return avcDecoderConfigurationRecord.hasExts; + } + + public void setHasExts(boolean hasExts) { + this.avcDecoderConfigurationRecord.hasExts = hasExts; + } + + @Override + public void _parseDetails(ByteBuffer content) { + avcDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(content); + } + + + @Override + public long getContentSize() { + return avcDecoderConfigurationRecord.getContentSize(); + } + + + @Override + public void getContent(ByteBuffer byteBuffer) { + avcDecoderConfigurationRecord.getContent(byteBuffer); + } + + // just to display sps in isoviewer no practical use + public String[] getSPS() { + return avcDecoderConfigurationRecord.getSPS(); + } + + public String[] getPPS() { + return avcDecoderConfigurationRecord.getPPS(); + } + + public List<String> getSequenceParameterSetsAsStrings() { + return avcDecoderConfigurationRecord.getSequenceParameterSetsAsStrings(); + } + + public List<String> getSequenceParameterSetExtsAsStrings() { + return avcDecoderConfigurationRecord.getSequenceParameterSetExtsAsStrings(); + } + + public List<String> getPictureParameterSetsAsStrings() { + return avcDecoderConfigurationRecord.getPictureParameterSetsAsStrings(); + } + + public AVCDecoderConfigurationRecord getavcDecoderConfigurationRecord() { + return avcDecoderConfigurationRecord; + } + + + public static class AVCDecoderConfigurationRecord { + public int configurationVersion; + public int avcProfileIndication; + public int profileCompatibility; + public int avcLevelIndication; + public int lengthSizeMinusOne; + public List<byte[]> sequenceParameterSets = new ArrayList<byte[]>(); + public List<byte[]> pictureParameterSets = new ArrayList<byte[]>(); + + public boolean hasExts = true; + public int chromaFormat = 1; + public int bitDepthLumaMinus8 = 0; + public int bitDepthChromaMinus8 = 0; + public List<byte[]> sequenceParameterSetExts = new ArrayList<byte[]>(); + + /** + * Just for non-spec-conform encoders + */ + public int lengthSizeMinusOnePaddingBits = 60; + 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(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(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(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 (byte[] sequenceParameterSetNALUnit : sequenceParameterSets) { + IsoTypeWriter.writeUInt16(byteBuffer, sequenceParameterSetNALUnit.length); + byteBuffer.put(sequenceParameterSetNALUnit); + } + IsoTypeWriter.writeUInt8(byteBuffer, pictureParameterSets.size()); + for (byte[] pictureParameterSetNALUnit : pictureParameterSets) { + IsoTypeWriter.writeUInt16(byteBuffer, pictureParameterSetNALUnit.length); + byteBuffer.put(pictureParameterSetNALUnit); + } + 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 (byte[] sequenceParameterSetExtNALUnit : sequenceParameterSetExts) { + IsoTypeWriter.writeUInt16(byteBuffer, sequenceParameterSetExtNALUnit.length); + byteBuffer.put(sequenceParameterSetExtNALUnit); + } + } + } + + public long getContentSize() { + long size = 5; + size += 1; // sequenceParamsetLength + for (byte[] sequenceParameterSetNALUnit : sequenceParameterSets) { + size += 2; //lengthSizeMinusOne field + size += sequenceParameterSetNALUnit.length; + } + size += 1; // pictureParamsetLength + for (byte[] pictureParameterSetNALUnit : pictureParameterSets) { + size += 2; //lengthSizeMinusOne field + size += pictureParameterSetNALUnit.length; + } + if (hasExts && (avcProfileIndication == 100 || avcProfileIndication == 110 || avcProfileIndication == 122 || avcProfileIndication == 144)) { + size += 4; + for (byte[] sequenceParameterSetExtNALUnit : sequenceParameterSetExts) { + size += 2; + size += sequenceParameterSetExtNALUnit.length; + } + } + + return size; + } + + public String[] getPPS() { + ArrayList<String> l = new ArrayList<String>(); + for (byte[] pictureParameterSet : pictureParameterSets) { + String details = "not parsable"; + try { + details = PictureParameterSet.read(pictureParameterSet).toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + l.add(details); + } + return l.toArray(new String[l.size()]); + } + + public String[] getSPS() { + ArrayList<String> l = new ArrayList<String>(); + for (byte[] sequenceParameterSet : sequenceParameterSets) { + String detail = "not parsable"; + try { + detail = SeqParameterSet.read(new ByteArrayInputStream(sequenceParameterSet)).toString(); + } catch (IOException e) { + + } + l.add(detail); + } + return l.toArray(new String[l.size()]); + } + + public List<String> getSequenceParameterSetsAsStrings() { + List <String> result = new ArrayList<String>(sequenceParameterSets.size()); + for (byte[] parameterSet : sequenceParameterSets) { + result.add(Hex.encodeHex(parameterSet)); + } + return result; + } + + public List<String> getSequenceParameterSetExtsAsStrings() { + List <String> result = new ArrayList<String>(sequenceParameterSetExts.size()); + for (byte[] parameterSet : sequenceParameterSetExts) { + result.add(Hex.encodeHex(parameterSet)); + } + return result; + } + + public List<String> getPictureParameterSetsAsStrings() { + List <String> result = new ArrayList<String>(pictureParameterSets.size()); + for (byte[] parameterSet : pictureParameterSets) { + result.add(Hex.encodeHex(parameterSet)); + } + return result; + } + + } +} + |