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
17package com.coremedia.iso.boxes.h264;
18
19import com.coremedia.iso.Hex;
20import com.coremedia.iso.IsoTypeReader;
21import com.coremedia.iso.IsoTypeWriter;
22import com.googlecode.mp4parser.AbstractBox;
23import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
24import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitWriterBuffer;
25import com.googlecode.mp4parser.h264.model.PictureParameterSet;
26import com.googlecode.mp4parser.h264.model.SeqParameterSet;
27
28import java.io.ByteArrayInputStream;
29import java.io.IOException;
30import java.nio.ByteBuffer;
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.List;
34
35/**
36 * Defined in ISO/IEC 14496-15:2004.
37 */
38public 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