summaryrefslogtreecommitdiff
path: root/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java')
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java292
1 files changed, 292 insertions, 0 deletions
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java
new file mode 100644
index 0000000..df51a1a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2012 castLabs GmbH, Berlin
+ *
+ * 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.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.AC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.*;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.*;
+
+/**
+ */
+public class AACTrackImpl extends AbstractTrack {
+ public static Map<Integer, Integer> samplingFrequencyIndexMap = new HashMap<Integer, Integer>();
+
+ static {
+ samplingFrequencyIndexMap.put(96000, 0);
+ samplingFrequencyIndexMap.put(88200, 1);
+ samplingFrequencyIndexMap.put(64000, 2);
+ samplingFrequencyIndexMap.put(48000, 3);
+ samplingFrequencyIndexMap.put(44100, 4);
+ samplingFrequencyIndexMap.put(32000, 5);
+ samplingFrequencyIndexMap.put(24000, 6);
+ samplingFrequencyIndexMap.put(22050, 7);
+ samplingFrequencyIndexMap.put(16000, 8);
+ samplingFrequencyIndexMap.put(12000, 9);
+ samplingFrequencyIndexMap.put(11025, 10);
+ samplingFrequencyIndexMap.put(8000, 11);
+ samplingFrequencyIndexMap.put(0x0, 96000);
+ samplingFrequencyIndexMap.put(0x1, 88200);
+ samplingFrequencyIndexMap.put(0x2, 64000);
+ samplingFrequencyIndexMap.put(0x3, 48000);
+ samplingFrequencyIndexMap.put(0x4, 44100);
+ samplingFrequencyIndexMap.put(0x5, 32000);
+ samplingFrequencyIndexMap.put(0x6, 24000);
+ samplingFrequencyIndexMap.put(0x7, 22050);
+ samplingFrequencyIndexMap.put(0x8, 16000);
+ samplingFrequencyIndexMap.put(0x9, 12000);
+ samplingFrequencyIndexMap.put(0xa, 11025);
+ samplingFrequencyIndexMap.put(0xb, 8000);
+ }
+
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int channelCount;
+ int channelconfig;
+
+ int bufferSizeDB;
+ long maxBitRate;
+ long avgBitRate;
+
+ private BufferedInputStream inputStream;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+ List<TimeToSampleBox.Entry> stts;
+ private String lang = "und";
+
+
+ public AACTrackImpl(InputStream inputStream, String lang) throws IOException {
+ this.lang = lang;
+ parse(inputStream);
+ }
+
+ public AACTrackImpl(InputStream inputStream) throws IOException {
+ parse(inputStream);
+ }
+
+ private void parse(InputStream inputStream) throws IOException {
+ this.inputStream = new BufferedInputStream(inputStream);
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+
+ double packetsPerSecond = (double)samplerate / 1024.0;
+ double duration = samples.size() / packetsPerSecond;
+
+ long dataSize = 0;
+ LinkedList<Integer> queue = new LinkedList<Integer>();
+ for (int i = 0; i < samples.size(); i++) {
+ int size = samples.get(i).capacity();
+ dataSize += size;
+ queue.add(size);
+ while (queue.size() > packetsPerSecond) {
+ queue.pop();
+ }
+ if (queue.size() == (int) packetsPerSecond) {
+ int currSize = 0;
+ for (int j = 0 ; j < queue.size(); j++) {
+ currSize += queue.get(j);
+ }
+ double currBitrate = 8.0 * currSize / queue.size() * packetsPerSecond;
+ if (currBitrate > maxBitRate) {
+ maxBitRate = (int)currBitrate;
+ }
+ }
+ }
+
+ avgBitRate = (int) (8 * dataSize / duration);
+
+ bufferSizeDB = 1536; /* TODO: Calcultate this somehow! */
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("mp4a");
+ audioSampleEntry.setChannelCount(2);
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+
+ ESDescriptorBox esds = new ESDescriptorBox();
+ ESDescriptor descriptor = new ESDescriptor();
+ descriptor.setEsId(0);
+
+ SLConfigDescriptor slConfigDescriptor = new SLConfigDescriptor();
+ slConfigDescriptor.setPredefined(2);
+ descriptor.setSlConfigDescriptor(slConfigDescriptor);
+
+ DecoderConfigDescriptor decoderConfigDescriptor = new DecoderConfigDescriptor();
+ decoderConfigDescriptor.setObjectTypeIndication(0x40);
+ decoderConfigDescriptor.setStreamType(5);
+ decoderConfigDescriptor.setBufferSizeDB(bufferSizeDB);
+ decoderConfigDescriptor.setMaxBitRate(maxBitRate);
+ decoderConfigDescriptor.setAvgBitRate(avgBitRate);
+
+ AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig();
+ audioSpecificConfig.setAudioObjectType(2); // AAC LC
+ audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get(samplerate));
+ audioSpecificConfig.setChannelConfiguration(channelconfig);
+ decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig);
+
+ descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor);
+
+ ByteBuffer data = descriptor.serialize();
+ esds.setData(data);
+ audioSampleEntry.addBox(esds);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "soun";
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public Box getMediaHeaderBox() {
+ return new SoundMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private boolean readVariables() throws IOException {
+ byte[] data = new byte[100];
+ inputStream.mark(100);
+ if (100 != inputStream.read(data, 0, 100)) {
+ return false;
+ }
+ inputStream.reset(); // Rewind
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(12);
+ if (syncword != 0xfff) {
+ return false;
+ }
+ int id = brb.readBits(1);
+ int layer = brb.readBits(2);
+ int protectionAbsent = brb.readBits(1);
+ int profile = brb.readBits(2);
+ samplerate = samplingFrequencyIndexMap.get(brb.readBits(4));
+ brb.readBits(1);
+ channelconfig = brb.readBits(3);
+ int original = brb.readBits(1);
+ int home = brb.readBits(1);
+ int emphasis = brb.readBits(2);
+
+ return true;
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+
+ readSamples = true;
+ byte[] header = new byte[15];
+ boolean ret = false;
+ inputStream.mark(15);
+ while (-1 != inputStream.read(header)) {
+ ret = true;
+ ByteBuffer bb = ByteBuffer.wrap(header);
+ inputStream.reset();
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(12);
+ if (syncword != 0xfff) {
+ return false;
+ }
+ brb.readBits(3);
+ int protectionAbsent = brb.readBits(1);
+ brb.readBits(14);
+ int frameSize = brb.readBits(13);
+ int bufferFullness = brb.readBits(11);
+ int noBlocks = brb.readBits(2);
+ int used = (int) Math.ceil(brb.getPosition() / 8.0);
+ if (protectionAbsent == 0) {
+ used += 2;
+ }
+ inputStream.skip(used);
+ frameSize -= used;
+// System.out.println("Size: " + frameSize + " fullness: " + bufferFullness + " no blocks: " + noBlocks);
+ byte[] data = new byte[frameSize];
+ inputStream.read(data);
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1024));
+ inputStream.mark(15);
+ }
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ return "AACTrackImpl{" +
+ "samplerate=" + samplerate +
+ ", bitrate=" + bitrate +
+ ", channelCount=" + channelCount +
+ ", channelconfig=" + channelconfig +
+ '}';
+ }
+}
+