AACTrackImpl.java revision dd9eb897ee7c7b507cbdcf80263bb4b5de6966bf
1/* 2 * Copyright 2012 castLabs GmbH, Berlin 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 */ 16package com.googlecode.mp4parser.authoring.tracks; 17 18import com.coremedia.iso.boxes.*; 19import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry; 20import com.googlecode.mp4parser.authoring.AbstractTrack; 21import com.googlecode.mp4parser.authoring.TrackMetaData; 22import com.googlecode.mp4parser.boxes.AC3SpecificBox; 23import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox; 24import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.*; 25 26import java.io.BufferedInputStream; 27import java.io.IOException; 28import java.io.InputStream; 29import java.nio.ByteBuffer; 30import java.util.*; 31 32/** 33 */ 34public class AACTrackImpl extends AbstractTrack { 35 public static Map<Integer, Integer> samplingFrequencyIndexMap = new HashMap<Integer, Integer>(); 36 37 static { 38 samplingFrequencyIndexMap.put(96000, 0); 39 samplingFrequencyIndexMap.put(88200, 1); 40 samplingFrequencyIndexMap.put(64000, 2); 41 samplingFrequencyIndexMap.put(48000, 3); 42 samplingFrequencyIndexMap.put(44100, 4); 43 samplingFrequencyIndexMap.put(32000, 5); 44 samplingFrequencyIndexMap.put(24000, 6); 45 samplingFrequencyIndexMap.put(22050, 7); 46 samplingFrequencyIndexMap.put(16000, 8); 47 samplingFrequencyIndexMap.put(12000, 9); 48 samplingFrequencyIndexMap.put(11025, 10); 49 samplingFrequencyIndexMap.put(8000, 11); 50 samplingFrequencyIndexMap.put(0x0, 96000); 51 samplingFrequencyIndexMap.put(0x1, 88200); 52 samplingFrequencyIndexMap.put(0x2, 64000); 53 samplingFrequencyIndexMap.put(0x3, 48000); 54 samplingFrequencyIndexMap.put(0x4, 44100); 55 samplingFrequencyIndexMap.put(0x5, 32000); 56 samplingFrequencyIndexMap.put(0x6, 24000); 57 samplingFrequencyIndexMap.put(0x7, 22050); 58 samplingFrequencyIndexMap.put(0x8, 16000); 59 samplingFrequencyIndexMap.put(0x9, 12000); 60 samplingFrequencyIndexMap.put(0xa, 11025); 61 samplingFrequencyIndexMap.put(0xb, 8000); 62 } 63 64 TrackMetaData trackMetaData = new TrackMetaData(); 65 SampleDescriptionBox sampleDescriptionBox; 66 67 int samplerate; 68 int bitrate; 69 int channelCount; 70 int channelconfig; 71 72 int bufferSizeDB; 73 long maxBitRate; 74 long avgBitRate; 75 76 private BufferedInputStream inputStream; 77 private List<ByteBuffer> samples; 78 boolean readSamples = false; 79 List<TimeToSampleBox.Entry> stts; 80 private String lang = "und"; 81 82 83 public AACTrackImpl(InputStream inputStream, String lang) throws IOException { 84 this.lang = lang; 85 parse(inputStream); 86 } 87 88 public AACTrackImpl(InputStream inputStream) throws IOException { 89 parse(inputStream); 90 } 91 92 private void parse(InputStream inputStream) throws IOException { 93 this.inputStream = new BufferedInputStream(inputStream); 94 stts = new LinkedList<TimeToSampleBox.Entry>(); 95 96 if (!readVariables()) { 97 throw new IOException(); 98 } 99 100 samples = new LinkedList<ByteBuffer>(); 101 if (!readSamples()) { 102 throw new IOException(); 103 } 104 105 double packetsPerSecond = (double)samplerate / 1024.0; 106 double duration = samples.size() / packetsPerSecond; 107 108 long dataSize = 0; 109 LinkedList<Integer> queue = new LinkedList<Integer>(); 110 for (int i = 0; i < samples.size(); i++) { 111 int size = samples.get(i).capacity(); 112 dataSize += size; 113 queue.add(size); 114 while (queue.size() > packetsPerSecond) { 115 queue.pop(); 116 } 117 if (queue.size() == (int) packetsPerSecond) { 118 int currSize = 0; 119 for (int j = 0 ; j < queue.size(); j++) { 120 currSize += queue.get(j); 121 } 122 double currBitrate = 8.0 * currSize / queue.size() * packetsPerSecond; 123 if (currBitrate > maxBitRate) { 124 maxBitRate = (int)currBitrate; 125 } 126 } 127 } 128 129 avgBitRate = (int) (8 * dataSize / duration); 130 131 bufferSizeDB = 1536; /* TODO: Calcultate this somehow! */ 132 133 sampleDescriptionBox = new SampleDescriptionBox(); 134 AudioSampleEntry audioSampleEntry = new AudioSampleEntry("mp4a"); 135 audioSampleEntry.setChannelCount(2); 136 audioSampleEntry.setSampleRate(samplerate); 137 audioSampleEntry.setDataReferenceIndex(1); 138 audioSampleEntry.setSampleSize(16); 139 140 141 ESDescriptorBox esds = new ESDescriptorBox(); 142 ESDescriptor descriptor = new ESDescriptor(); 143 descriptor.setEsId(0); 144 145 SLConfigDescriptor slConfigDescriptor = new SLConfigDescriptor(); 146 slConfigDescriptor.setPredefined(2); 147 descriptor.setSlConfigDescriptor(slConfigDescriptor); 148 149 DecoderConfigDescriptor decoderConfigDescriptor = new DecoderConfigDescriptor(); 150 decoderConfigDescriptor.setObjectTypeIndication(0x40); 151 decoderConfigDescriptor.setStreamType(5); 152 decoderConfigDescriptor.setBufferSizeDB(bufferSizeDB); 153 decoderConfigDescriptor.setMaxBitRate(maxBitRate); 154 decoderConfigDescriptor.setAvgBitRate(avgBitRate); 155 156 AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig(); 157 audioSpecificConfig.setAudioObjectType(2); // AAC LC 158 audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get(samplerate)); 159 audioSpecificConfig.setChannelConfiguration(channelconfig); 160 decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig); 161 162 descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor); 163 164 ByteBuffer data = descriptor.serialize(); 165 esds.setData(data); 166 audioSampleEntry.addBox(esds); 167 sampleDescriptionBox.addBox(audioSampleEntry); 168 169 trackMetaData.setCreationTime(new Date()); 170 trackMetaData.setModificationTime(new Date()); 171 trackMetaData.setLanguage(lang); 172 trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale 173 } 174 175 public SampleDescriptionBox getSampleDescriptionBox() { 176 return sampleDescriptionBox; 177 } 178 179 public List<TimeToSampleBox.Entry> getDecodingTimeEntries() { 180 return stts; 181 } 182 183 public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() { 184 return null; 185 } 186 187 public long[] getSyncSamples() { 188 return null; 189 } 190 191 public List<SampleDependencyTypeBox.Entry> getSampleDependencies() { 192 return null; 193 } 194 195 public TrackMetaData getTrackMetaData() { 196 return trackMetaData; 197 } 198 199 public String getHandler() { 200 return "soun"; 201 } 202 203 public List<ByteBuffer> getSamples() { 204 return samples; 205 } 206 207 public Box getMediaHeaderBox() { 208 return new SoundMediaHeaderBox(); 209 } 210 211 public SubSampleInformationBox getSubsampleInformationBox() { 212 return null; 213 } 214 215 private boolean readVariables() throws IOException { 216 byte[] data = new byte[100]; 217 inputStream.mark(100); 218 if (100 != inputStream.read(data, 0, 100)) { 219 return false; 220 } 221 inputStream.reset(); // Rewind 222 ByteBuffer bb = ByteBuffer.wrap(data); 223 BitReaderBuffer brb = new BitReaderBuffer(bb); 224 int syncword = brb.readBits(12); 225 if (syncword != 0xfff) { 226 return false; 227 } 228 int id = brb.readBits(1); 229 int layer = brb.readBits(2); 230 int protectionAbsent = brb.readBits(1); 231 int profile = brb.readBits(2); 232 samplerate = samplingFrequencyIndexMap.get(brb.readBits(4)); 233 brb.readBits(1); 234 channelconfig = brb.readBits(3); 235 int original = brb.readBits(1); 236 int home = brb.readBits(1); 237 int emphasis = brb.readBits(2); 238 239 return true; 240 } 241 242 private boolean readSamples() throws IOException { 243 if (readSamples) { 244 return true; 245 } 246 247 readSamples = true; 248 byte[] header = new byte[15]; 249 boolean ret = false; 250 inputStream.mark(15); 251 while (-1 != inputStream.read(header)) { 252 ret = true; 253 ByteBuffer bb = ByteBuffer.wrap(header); 254 inputStream.reset(); 255 BitReaderBuffer brb = new BitReaderBuffer(bb); 256 int syncword = brb.readBits(12); 257 if (syncword != 0xfff) { 258 return false; 259 } 260 brb.readBits(3); 261 int protectionAbsent = brb.readBits(1); 262 brb.readBits(14); 263 int frameSize = brb.readBits(13); 264 int bufferFullness = brb.readBits(11); 265 int noBlocks = brb.readBits(2); 266 int used = (int) Math.ceil(brb.getPosition() / 8.0); 267 if (protectionAbsent == 0) { 268 used += 2; 269 } 270 inputStream.skip(used); 271 frameSize -= used; 272// System.out.println("Size: " + frameSize + " fullness: " + bufferFullness + " no blocks: " + noBlocks); 273 byte[] data = new byte[frameSize]; 274 inputStream.read(data); 275 samples.add(ByteBuffer.wrap(data)); 276 stts.add(new TimeToSampleBox.Entry(1, 1024)); 277 inputStream.mark(15); 278 } 279 return ret; 280 } 281 282 @Override 283 public String toString() { 284 return "AACTrackImpl{" + 285 "samplerate=" + samplerate + 286 ", bitrate=" + bitrate + 287 ", channelCount=" + channelCount + 288 ", channelconfig=" + channelconfig + 289 '}'; 290 } 291} 292 293