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 44 public AvcConfigurationBox() { 45 super(TYPE); 46 } 47 48 public int getConfigurationVersion() { 49 return avcDecoderConfigurationRecord.configurationVersion; 50 } 51 52 public int getAvcProfileIndication() { 53 return avcDecoderConfigurationRecord.avcProfileIndication; 54 } 55 56 public int getProfileCompatibility() { 57 return avcDecoderConfigurationRecord.profileCompatibility; 58 } 59 60 public int getAvcLevelIndication() { 61 return avcDecoderConfigurationRecord.avcLevelIndication; 62 } 63 64 public int getLengthSizeMinusOne() { 65 return avcDecoderConfigurationRecord.lengthSizeMinusOne; 66 } 67 68 public List<byte[]> getSequenceParameterSets() { 69 return Collections.unmodifiableList(avcDecoderConfigurationRecord.sequenceParameterSets); 70 } 71 72 public List<byte[]> getPictureParameterSets() { 73 return Collections.unmodifiableList(avcDecoderConfigurationRecord.pictureParameterSets); 74 } 75 76 public void setConfigurationVersion(int configurationVersion) { 77 this.avcDecoderConfigurationRecord.configurationVersion = configurationVersion; 78 } 79 80 public void setAvcProfileIndication(int avcProfileIndication) { 81 this.avcDecoderConfigurationRecord.avcProfileIndication = avcProfileIndication; 82 } 83 84 public void setProfileCompatibility(int profileCompatibility) { 85 this.avcDecoderConfigurationRecord.profileCompatibility = profileCompatibility; 86 } 87 88 public void setAvcLevelIndication(int avcLevelIndication) { 89 this.avcDecoderConfigurationRecord.avcLevelIndication = avcLevelIndication; 90 } 91 92 public void setLengthSizeMinusOne(int lengthSizeMinusOne) { 93 this.avcDecoderConfigurationRecord.lengthSizeMinusOne = lengthSizeMinusOne; 94 } 95 96 public void setSequenceParameterSets(List<byte[]> sequenceParameterSets) { 97 this.avcDecoderConfigurationRecord.sequenceParameterSets = sequenceParameterSets; 98 } 99 100 public void setPictureParameterSets(List<byte[]> pictureParameterSets) { 101 this.avcDecoderConfigurationRecord.pictureParameterSets = pictureParameterSets; 102 } 103 104 public int getChromaFormat() { 105 return avcDecoderConfigurationRecord.chromaFormat; 106 } 107 108 public void setChromaFormat(int chromaFormat) { 109 this.avcDecoderConfigurationRecord.chromaFormat = chromaFormat; 110 } 111 112 public int getBitDepthLumaMinus8() { 113 return avcDecoderConfigurationRecord.bitDepthLumaMinus8; 114 } 115 116 public void setBitDepthLumaMinus8(int bitDepthLumaMinus8) { 117 this.avcDecoderConfigurationRecord.bitDepthLumaMinus8 = bitDepthLumaMinus8; 118 } 119 120 public int getBitDepthChromaMinus8() { 121 return avcDecoderConfigurationRecord.bitDepthChromaMinus8; 122 } 123 124 public void setBitDepthChromaMinus8(int bitDepthChromaMinus8) { 125 this.avcDecoderConfigurationRecord.bitDepthChromaMinus8 = bitDepthChromaMinus8; 126 } 127 128 public List<byte[]> getSequenceParameterSetExts() { 129 return avcDecoderConfigurationRecord.sequenceParameterSetExts; 130 } 131 132 public void setSequenceParameterSetExts(List<byte[]> sequenceParameterSetExts) { 133 this.avcDecoderConfigurationRecord.sequenceParameterSetExts = sequenceParameterSetExts; 134 } 135 136 public boolean hasExts() { 137 return avcDecoderConfigurationRecord.hasExts; 138 } 139 140 public void setHasExts(boolean hasExts) { 141 this.avcDecoderConfigurationRecord.hasExts = hasExts; 142 } 143 144 @Override 145 public void _parseDetails(ByteBuffer content) { 146 avcDecoderConfigurationRecord = new AVCDecoderConfigurationRecord(content); 147 } 148 149 150 @Override 151 public long getContentSize() { 152 return avcDecoderConfigurationRecord.getContentSize(); 153 } 154 155 156 @Override 157 public void getContent(ByteBuffer byteBuffer) { 158 avcDecoderConfigurationRecord.getContent(byteBuffer); 159 } 160 161 // just to display sps in isoviewer no practical use 162 public String[] getSPS() { 163 return avcDecoderConfigurationRecord.getSPS(); 164 } 165 166 public String[] getPPS() { 167 return avcDecoderConfigurationRecord.getPPS(); 168 } 169 170 public List<String> getSequenceParameterSetsAsStrings() { 171 return avcDecoderConfigurationRecord.getSequenceParameterSetsAsStrings(); 172 } 173 174 public List<String> getSequenceParameterSetExtsAsStrings() { 175 return avcDecoderConfigurationRecord.getSequenceParameterSetExtsAsStrings(); 176 } 177 178 public List<String> getPictureParameterSetsAsStrings() { 179 return avcDecoderConfigurationRecord.getPictureParameterSetsAsStrings(); 180 } 181 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 211 public AVCDecoderConfigurationRecord() { 212 } 213 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 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 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 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 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 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 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 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