blob: 52f369526a2aa1af411ac93f9f114ec68716af81 [file] [log] [blame]
/*
* 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;
}
}
}