1 /* 2 * Copyright 2008 CoreMedia AG, Hamburg 3 * 4 * Licensed under the Apache License, Version 2.0 (the License); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an AS IS BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.coremedia.iso.boxes.h264; 18 19 import com.coremedia.iso.Hex; 20 import com.coremedia.iso.IsoTypeReader; 21 import com.coremedia.iso.IsoTypeWriter; 22 import com.googlecode.mp4parser.AbstractBox; 23 import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer; 24 import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer; 25 import com.googlecode.mp4parser.h264.model.PictureParameterSet; 26 import com.googlecode.mp4parser.h264.model.SeqParameterSet; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.IOException; 30 import java.nio.ByteBuffer; 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.List; 34 35 /** 36 * Defined in ISO/IEC 14496-15:2004. 37 */ 38 public final class AvcConfigurationBox extends AbstractBox { 39 public static final String TYPE = "avcC"; 40 41 public AVCDecoderConfigurationRecord avcDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(); 42 43 AvcConfigurationBox()44 public AvcConfigurationBox() { 45 super(TYPE); 46 } 47 getConfigurationVersion()48 public int getConfigurationVersion() { 49 return avcDecoderConfigurationRecord.configurationVersion; 50 } 51 getAvcProfileIndication()52 public int getAvcProfileIndication() { 53 return avcDecoderConfigurationRecord.avcProfileIndication; 54 } 55 getProfileCompatibility()56 public int getProfileCompatibility() { 57 return avcDecoderConfigurationRecord.profileCompatibility; 58 } 59 getAvcLevelIndication()60 public int getAvcLevelIndication() { 61 return avcDecoderConfigurationRecord.avcLevelIndication; 62 } 63 getLengthSizeMinusOne()64 public int getLengthSizeMinusOne() { 65 return avcDecoderConfigurationRecord.lengthSizeMinusOne; 66 } 67 getSequenceParameterSets()68 public List<byte[]> getSequenceParameterSets() { 69 return Collections.unmodifiableList(avcDecoderConfigurationRecord.sequenceParameterSets); 70 } 71 getPictureParameterSets()72 public List<byte[]> getPictureParameterSets() { 73 return Collections.unmodifiableList(avcDecoderConfigurationRecord.pictureParameterSets); 74 } 75 setConfigurationVersion(int configurationVersion)76 public void setConfigurationVersion(int configurationVersion) { 77 this.avcDecoderConfigurationRecord.configurationVersion = configurationVersion; 78 } 79 setAvcProfileIndication(int avcProfileIndication)80 public void setAvcProfileIndication(int avcProfileIndication) { 81 this.avcDecoderConfigurationRecord.avcProfileIndication = avcProfileIndication; 82 } 83 setProfileCompatibility(int profileCompatibility)84 public void setProfileCompatibility(int profileCompatibility) { 85 this.avcDecoderConfigurationRecord.profileCompatibility = profileCompatibility; 86 } 87 setAvcLevelIndication(int avcLevelIndication)88 public void setAvcLevelIndication(int avcLevelIndication) { 89 this.avcDecoderConfigurationRecord.avcLevelIndication = avcLevelIndication; 90 } 91 setLengthSizeMinusOne(int lengthSizeMinusOne)92 public void setLengthSizeMinusOne(int lengthSizeMinusOne) { 93 this.avcDecoderConfigurationRecord.lengthSizeMinusOne = lengthSizeMinusOne; 94 } 95 setSequenceParameterSets(List<byte[]> sequenceParameterSets)96 public void setSequenceParameterSets(List<byte[]> sequenceParameterSets) { 97 this.avcDecoderConfigurationRecord.sequenceParameterSets = sequenceParameterSets; 98 } 99 setPictureParameterSets(List<byte[]> pictureParameterSets)100 public void setPictureParameterSets(List<byte[]> pictureParameterSets) { 101 this.avcDecoderConfigurationRecord.pictureParameterSets = pictureParameterSets; 102 } 103 getChromaFormat()104 public int getChromaFormat() { 105 return avcDecoderConfigurationRecord.chromaFormat; 106 } 107 setChromaFormat(int chromaFormat)108 public void setChromaFormat(int chromaFormat) { 109 this.avcDecoderConfigurationRecord.chromaFormat = chromaFormat; 110 } 111 getBitDepthLumaMinus8()112 public int getBitDepthLumaMinus8() { 113 return avcDecoderConfigurationRecord.bitDepthLumaMinus8; 114 } 115 setBitDepthLumaMinus8(int bitDepthLumaMinus8)116 public void setBitDepthLumaMinus8(int bitDepthLumaMinus8) { 117 this.avcDecoderConfigurationRecord.bitDepthLumaMinus8 = bitDepthLumaMinus8; 118 } 119 getBitDepthChromaMinus8()120 public int getBitDepthChromaMinus8() { 121 return avcDecoderConfigurationRecord.bitDepthChromaMinus8; 122 } 123 setBitDepthChromaMinus8(int bitDepthChromaMinus8)124 public void setBitDepthChromaMinus8(int bitDepthChromaMinus8) { 125 this.avcDecoderConfigurationRecord.bitDepthChromaMinus8 = bitDepthChromaMinus8; 126 } 127 getSequenceParameterSetExts()128 public List<byte[]> getSequenceParameterSetExts() { 129 return avcDecoderConfigurationRecord.sequenceParameterSetExts; 130 } 131 setSequenceParameterSetExts(List<byte[]> sequenceParameterSetExts)132 public void setSequenceParameterSetExts(List<byte[]> sequenceParameterSetExts) { 133 this.avcDecoderConfigurationRecord.sequenceParameterSetExts = sequenceParameterSetExts; 134 } 135 hasExts()136 public boolean hasExts() { 137 return avcDecoderConfigurationRecord.hasExts; 138 } 139 setHasExts(boolean hasExts)140 public void setHasExts(boolean hasExts) { 141 this.avcDecoderConfigurationRecord.hasExts = hasExts; 142 } 143 144 @Override _parseDetails(ByteBuffer content)145 public void _parseDetails(ByteBuffer content) { 146 avcDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(content); 147 } 148 149 150 @Override getContentSize()151 public long getContentSize() { 152 return avcDecoderConfigurationRecord.getContentSize(); 153 } 154 155 156 @Override getContent(ByteBuffer byteBuffer)157 public void getContent(ByteBuffer byteBuffer) { 158 avcDecoderConfigurationRecord.getContent(byteBuffer); 159 } 160 161 // just to display sps in isoviewer no practical use getSPS()162 public String[] getSPS() { 163 return avcDecoderConfigurationRecord.getSPS(); 164 } 165 getPPS()166 public String[] getPPS() { 167 return avcDecoderConfigurationRecord.getPPS(); 168 } 169 getSequenceParameterSetsAsStrings()170 public List<String> getSequenceParameterSetsAsStrings() { 171 return avcDecoderConfigurationRecord.getSequenceParameterSetsAsStrings(); 172 } 173 getSequenceParameterSetExtsAsStrings()174 public List<String> getSequenceParameterSetExtsAsStrings() { 175 return avcDecoderConfigurationRecord.getSequenceParameterSetExtsAsStrings(); 176 } 177 getPictureParameterSetsAsStrings()178 public List<String> getPictureParameterSetsAsStrings() { 179 return avcDecoderConfigurationRecord.getPictureParameterSetsAsStrings(); 180 } 181 getavcDecoderConfigurationRecord()182 public AVCDecoderConfigurationRecord getavcDecoderConfigurationRecord() { 183 return avcDecoderConfigurationRecord; 184 } 185 186 187 public static class AVCDecoderConfigurationRecord { 188 public int configurationVersion; 189 public int avcProfileIndication; 190 public int profileCompatibility; 191 public int avcLevelIndication; 192 public int lengthSizeMinusOne; 193 public List<byte[]> sequenceParameterSets = new ArrayList<byte[]>(); 194 public List<byte[]> pictureParameterSets = new ArrayList<byte[]>(); 195 196 public boolean hasExts = true; 197 public int chromaFormat = 1; 198 public int bitDepthLumaMinus8 = 0; 199 public int bitDepthChromaMinus8 = 0; 200 public List<byte[]> sequenceParameterSetExts = new ArrayList<byte[]>(); 201 202 /** 203 * Just for non-spec-conform encoders 204 */ 205 public int lengthSizeMinusOnePaddingBits = 60; 206 public int numberOfSequenceParameterSetsPaddingBits = 7; 207 public int chromaFormatPaddingBits = 31; 208 public int bitDepthLumaMinus8PaddingBits = 31; 209 public int bitDepthChromaMinus8PaddingBits = 31; 210 AVCDecoderConfigurationRecord()211 public AVCDecoderConfigurationRecord() { 212 } 213 AVCDecoderConfigurationRecord(ByteBuffer content)214 public AVCDecoderConfigurationRecord(ByteBuffer content) { 215 configurationVersion = IsoTypeReader.readUInt8(content); 216 avcProfileIndication = IsoTypeReader.readUInt8(content); 217 profileCompatibility = IsoTypeReader.readUInt8(content); 218 avcLevelIndication = IsoTypeReader.readUInt8(content); 219 BitReaderBuffer brb = new BitReaderBuffer(content); 220 lengthSizeMinusOnePaddingBits = brb.readBits(6); 221 lengthSizeMinusOne = brb.readBits(2); 222 numberOfSequenceParameterSetsPaddingBits = brb.readBits(3); 223 int numberOfSeuqenceParameterSets = brb.readBits(5); 224 for (int i = 0; i < numberOfSeuqenceParameterSets; i++) { 225 int sequenceParameterSetLength = IsoTypeReader.readUInt16(content); 226 227 byte[] sequenceParameterSetNALUnit = new byte[sequenceParameterSetLength]; 228 content.get(sequenceParameterSetNALUnit); 229 sequenceParameterSets.add(sequenceParameterSetNALUnit); 230 } 231 long numberOfPictureParameterSets = IsoTypeReader.readUInt8(content); 232 for (int i = 0; i < numberOfPictureParameterSets; i++) { 233 int pictureParameterSetLength = IsoTypeReader.readUInt16(content); 234 byte[] pictureParameterSetNALUnit = new byte[pictureParameterSetLength]; 235 content.get(pictureParameterSetNALUnit); 236 pictureParameterSets.add(pictureParameterSetNALUnit); 237 } 238 if (content.remaining() < 4) { 239 hasExts = false; 240 } 241 if (hasExts && (avcProfileIndication == 100 || avcProfileIndication == 110 || avcProfileIndication == 122 || avcProfileIndication == 144)) { 242 // 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. 243 // So we need to store all bits 244 brb = new BitReaderBuffer(content); 245 chromaFormatPaddingBits = brb.readBits(6); 246 chromaFormat = brb.readBits(2); 247 bitDepthLumaMinus8PaddingBits = brb.readBits(5); 248 bitDepthLumaMinus8 = brb.readBits(3); 249 bitDepthChromaMinus8PaddingBits = brb.readBits(5); 250 bitDepthChromaMinus8 = brb.readBits(3); 251 long numOfSequenceParameterSetExt = IsoTypeReader.readUInt8(content); 252 for (int i = 0; i < numOfSequenceParameterSetExt; i++) { 253 int sequenceParameterSetExtLength = IsoTypeReader.readUInt16(content); 254 byte[] sequenceParameterSetExtNALUnit = new byte[sequenceParameterSetExtLength]; 255 content.get(sequenceParameterSetExtNALUnit); 256 sequenceParameterSetExts.add(sequenceParameterSetExtNALUnit); 257 } 258 } else { 259 chromaFormat = -1; 260 bitDepthLumaMinus8 = -1; 261 bitDepthChromaMinus8 = -1; 262 } 263 } 264 getContent(ByteBuffer byteBuffer)265 public void getContent(ByteBuffer byteBuffer) { 266 IsoTypeWriter.writeUInt8(byteBuffer, configurationVersion); 267 IsoTypeWriter.writeUInt8(byteBuffer, avcProfileIndication); 268 IsoTypeWriter.writeUInt8(byteBuffer, profileCompatibility); 269 IsoTypeWriter.writeUInt8(byteBuffer, avcLevelIndication); 270 BitWriterBuffer bwb = new BitWriterBuffer(byteBuffer); 271 bwb.writeBits(lengthSizeMinusOnePaddingBits, 6); 272 bwb.writeBits(lengthSizeMinusOne, 2); 273 bwb.writeBits(numberOfSequenceParameterSetsPaddingBits, 3); 274 bwb.writeBits(pictureParameterSets.size(), 5); 275 for (byte[] sequenceParameterSetNALUnit : sequenceParameterSets) { 276 IsoTypeWriter.writeUInt16(byteBuffer, sequenceParameterSetNALUnit.length); 277 byteBuffer.put(sequenceParameterSetNALUnit); 278 } 279 IsoTypeWriter.writeUInt8(byteBuffer, pictureParameterSets.size()); 280 for (byte[] pictureParameterSetNALUnit : pictureParameterSets) { 281 IsoTypeWriter.writeUInt16(byteBuffer, pictureParameterSetNALUnit.length); 282 byteBuffer.put(pictureParameterSetNALUnit); 283 } 284 if (hasExts && (avcProfileIndication == 100 || avcProfileIndication == 110 || avcProfileIndication == 122 || avcProfileIndication == 144)) { 285 286 bwb = new BitWriterBuffer(byteBuffer); 287 bwb.writeBits(chromaFormatPaddingBits, 6); 288 bwb.writeBits(chromaFormat, 2); 289 bwb.writeBits(bitDepthLumaMinus8PaddingBits, 5); 290 bwb.writeBits(bitDepthLumaMinus8, 3); 291 bwb.writeBits(bitDepthChromaMinus8PaddingBits, 5); 292 bwb.writeBits(bitDepthChromaMinus8, 3); 293 for (byte[] sequenceParameterSetExtNALUnit : sequenceParameterSetExts) { 294 IsoTypeWriter.writeUInt16(byteBuffer, sequenceParameterSetExtNALUnit.length); 295 byteBuffer.put(sequenceParameterSetExtNALUnit); 296 } 297 } 298 } 299 getContentSize()300 public long getContentSize() { 301 long size = 5; 302 size += 1; // sequenceParamsetLength 303 for (byte[] sequenceParameterSetNALUnit : sequenceParameterSets) { 304 size += 2; //lengthSizeMinusOne field 305 size += sequenceParameterSetNALUnit.length; 306 } 307 size += 1; // pictureParamsetLength 308 for (byte[] pictureParameterSetNALUnit : pictureParameterSets) { 309 size += 2; //lengthSizeMinusOne field 310 size += pictureParameterSetNALUnit.length; 311 } 312 if (hasExts && (avcProfileIndication == 100 || avcProfileIndication == 110 || avcProfileIndication == 122 || avcProfileIndication == 144)) { 313 size += 4; 314 for (byte[] sequenceParameterSetExtNALUnit : sequenceParameterSetExts) { 315 size += 2; 316 size += sequenceParameterSetExtNALUnit.length; 317 } 318 } 319 320 return size; 321 } 322 getPPS()323 public String[] getPPS() { 324 ArrayList<String> l = new ArrayList<String>(); 325 for (byte[] pictureParameterSet : pictureParameterSets) { 326 String details = "not parsable"; 327 try { 328 details = PictureParameterSet.read(pictureParameterSet).toString(); 329 } catch (IOException e) { 330 throw new RuntimeException(e); 331 } 332 333 l.add(details); 334 } 335 return l.toArray(new String[l.size()]); 336 } 337 getSPS()338 public String[] getSPS() { 339 ArrayList<String> l = new ArrayList<String>(); 340 for (byte[] sequenceParameterSet : sequenceParameterSets) { 341 String detail = "not parsable"; 342 try { 343 detail = SeqParameterSet.read(new ByteArrayInputStream(sequenceParameterSet)).toString(); 344 } catch (IOException e) { 345 346 } 347 l.add(detail); 348 } 349 return l.toArray(new String[l.size()]); 350 } 351 getSequenceParameterSetsAsStrings()352 public List<String> getSequenceParameterSetsAsStrings() { 353 List <String> result = new ArrayList<String>(sequenceParameterSets.size()); 354 for (byte[] parameterSet : sequenceParameterSets) { 355 result.add(Hex.encodeHex(parameterSet)); 356 } 357 return result; 358 } 359 getSequenceParameterSetExtsAsStrings()360 public List<String> getSequenceParameterSetExtsAsStrings() { 361 List <String> result = new ArrayList<String>(sequenceParameterSetExts.size()); 362 for (byte[] parameterSet : sequenceParameterSetExts) { 363 result.add(Hex.encodeHex(parameterSet)); 364 } 365 return result; 366 } 367 getPictureParameterSetsAsStrings()368 public List<String> getPictureParameterSetsAsStrings() { 369 List <String> result = new ArrayList<String>(pictureParameterSets.size()); 370 for (byte[] parameterSet : pictureParameterSets) { 371 result.add(Hex.encodeHex(parameterSet)); 372 } 373 return result; 374 } 375 376 } 377 } 378 379