summaryrefslogtreecommitdiff
path: root/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn
diff options
context:
space:
mode:
Diffstat (limited to 'isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn')
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/all-wcprops89
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/entries504
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AACTrackImpl.java.svn-base292
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AC3TrackImpl.java.svn-base513
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/Amf0Track.java.svn-base116
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AppendTrack.java.svn-base348
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ChangeTimeScaleTrack.java.svn-base203
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/CroppedTrack.java.svn-base151
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/DivideTimeScaleTrack.java.svn-base126
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/EC3TrackImpl.java.svn-base436
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/H264TrackImpl.java.svn-base740
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/MultiplyTimeScaleTrack.java.svn-base130
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/QuicktimeTextTrackImpl.java.svn-base165
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ReplaceSampleTrack.java.svn-base104
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/SilenceTrackImpl.java.svn-base98
-rw-r--r--isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/TextTrackImpl.java.svn-base165
16 files changed, 4180 insertions, 0 deletions
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/all-wcprops b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/all-wcprops
new file mode 100644
index 0000000..496d7bb
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/all-wcprops
@@ -0,0 +1,89 @@
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks
+END
+DivideTimeScaleTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/DivideTimeScaleTrack.java
+END
+CroppedTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/CroppedTrack.java
+END
+ChangeTimeScaleTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ChangeTimeScaleTrack.java
+END
+EC3TrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/EC3TrackImpl.java
+END
+ReplaceSampleTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/ReplaceSampleTrack.java
+END
+QuicktimeTextTrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/svn/!svn/ver/691/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/QuicktimeTextTrackImpl.java
+END
+Amf0Track.java
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/Amf0Track.java
+END
+SilenceTrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 111
+/svn/!svn/ver/698/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/SilenceTrackImpl.java
+END
+H264TrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/H264TrackImpl.java
+END
+TextTrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/svn/!svn/ver/684/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/TextTrackImpl.java
+END
+AACTrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AACTrackImpl.java
+END
+MultiplyTimeScaleTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/svn/!svn/ver/686/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/MultiplyTimeScaleTrack.java
+END
+AppendTrack.java
+K 25
+svn:wc:ra_dav:version-url
+V 106
+/svn/!svn/ver/714/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AppendTrack.java
+END
+AC3TrackImpl.java
+K 25
+svn:wc:ra_dav:version-url
+V 107
+/svn/!svn/ver/756/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/AC3TrackImpl.java
+END
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/entries b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/entries
new file mode 100644
index 0000000..dbe8ae3
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/entries
@@ -0,0 +1,504 @@
+10
+
+dir
+778
+http://mp4parser.googlecode.com/svn/trunk/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks
+http://mp4parser.googlecode.com/svn
+
+
+
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7decde4b-c250-0410-a0da-51896bc88be6
+
+DivideTimeScaleTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+6aa41cdb7489e16e879ddebd68b7ac52
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3893
+
+CroppedTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+5d9c65d9aac52a26372aa3fdf6f1b7ea
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6025
+
+ChangeTimeScaleTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+ba01d30ac4b7c4fa9a2c6538ee537594
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7152
+
+EC3TrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+1d568bfaa0f41c3771986a7790347072
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13623
+
+ReplaceSampleTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+f6846b7a262ab0e530da46f4d7e34850
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3139
+
+QuicktimeTextTrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+7ccd01a58545fb02b507b57892eb53e5
+2012-06-24T21:35:59.546504Z
+691
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5128
+
+Amf0Track.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+4718a34bc271adf4517de9829ac74a9d
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3858
+
+SilenceTrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+a897677e602dfa0d64af1b0d33a04ca8
+2012-06-26T08:37:32.910396Z
+698
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2623
+
+H264TrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+76abb4b21c13d11215a2ac4cb0c7e461
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+28816
+
+TextTrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+5a643c876754eb5c7bcf75b4b71114a1
+2012-06-24T14:45:45.932648Z
+684
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4963
+
+AACTrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+ece8d364a9a5aeabf6a281f6d428e3cf
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10097
+
+MultiplyTimeScaleTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+b0ea53239c124607a26a181f594f82a1
+2012-06-24T19:52:05.961412Z
+686
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4192
+
+AppendTrack.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+e7814aebc4500724771fd0582455a7ca
+2012-07-18T23:22:45.506793Z
+714
+Sebastian.Annies@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15783
+
+AC3TrackImpl.java
+file
+
+
+
+
+2012-09-14T17:27:50.507219Z
+fbd724739cd9a5f2b28f16b412b27309
+2012-08-17T01:19:11.953078Z
+756
+michael.stattmann@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+19303
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AACTrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AACTrackImpl.java.svn-base
new file mode 100644
index 0000000..df51a1a
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AACTrackImpl.java.svn-base
@@ -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 +
+ '}';
+ }
+}
+
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AC3TrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AC3TrackImpl.java.svn-base
new file mode 100644
index 0000000..5e5b2cd
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AC3TrackImpl.java.svn-base
@@ -0,0 +1,513 @@
+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.objectdescriptors.BitReaderBuffer;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class AC3TrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int channelCount;
+
+ int fscod;
+ int bsid;
+ int bsmod;
+ int acmod;
+ int lfeon;
+ int frmsizecod;
+
+ int frameSize;
+ int[][][][] bitRateAndFrameSizeTable;
+
+ private InputStream inputStream;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+ List<TimeToSampleBox.Entry> stts;
+ private String lang = "und";
+
+ public AC3TrackImpl(InputStream fin, String lang) throws IOException {
+ this.lang = lang;
+ parse(fin);
+ }
+
+ public AC3TrackImpl(InputStream fin) throws IOException {
+ parse(fin);
+ }
+
+ private void parse(InputStream fin) throws IOException {
+ inputStream = fin;
+ bitRateAndFrameSizeTable = new int[19][2][3][2];
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+ initBitRateAndFrameSizeTable();
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ac-3");
+ audioSampleEntry.setChannelCount(2); // According to ETSI TS 102 366 Annex F
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+ AC3SpecificBox ac3 = new AC3SpecificBox();
+ ac3.setAcmod(acmod);
+ ac3.setBitRateCode(frmsizecod >> 1);
+ ac3.setBsid(bsid);
+ ac3.setBsmod(bsmod);
+ ac3.setFscod(fscod);
+ ac3.setLfeon(lfeon);
+ ac3.setReserved(0);
+
+ audioSampleEntry.addBox(ac3);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+
+ return samples;
+ }
+
+ 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 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(16);
+ if (syncword != 0xb77) {
+ return false;
+ }
+ brb.readBits(16); // CRC-1
+ fscod = brb.readBits(2);
+
+ switch (fscod) {
+ case 0:
+ samplerate = 48000;
+ break;
+
+ case 1:
+ samplerate = 44100;
+ break;
+
+ case 2:
+ samplerate = 32000;
+ break;
+
+ case 3:
+ samplerate = 0;
+ break;
+
+ }
+ if (samplerate == 0) {
+ return false;
+ }
+
+ frmsizecod = brb.readBits(6);
+
+ if (!calcBitrateAndFrameSize(frmsizecod)) {
+ return false;
+ }
+
+ if (frameSize == 0) {
+ return false;
+ }
+ bsid = brb.readBits(5);
+ bsmod = brb.readBits(3);
+ acmod = brb.readBits(3);
+
+ if (bsid == 9) {
+ samplerate /= 2;
+ } else if (bsid != 8 && bsid != 6) {
+ return false;
+ }
+
+ if ((acmod != 1) && ((acmod & 1) == 1)) {
+ brb.readBits(2);
+ }
+
+ if (0 != (acmod & 4)) {
+ brb.readBits(2);
+ }
+
+ if (acmod == 2) {
+ brb.readBits(2);
+ }
+
+ switch (acmod) {
+ case 0:
+ channelCount = 2;
+ break;
+
+ case 1:
+ channelCount = 1;
+ break;
+
+ case 2:
+ channelCount = 2;
+ break;
+
+ case 3:
+ channelCount = 3;
+ break;
+
+ case 4:
+ channelCount = 3;
+ break;
+
+ case 5:
+ channelCount = 4;
+ break;
+
+ case 6:
+ channelCount = 4;
+ break;
+
+ case 7:
+ channelCount = 5;
+ break;
+
+ }
+
+ lfeon = brb.readBits(1);
+
+ if (lfeon == 1) {
+ channelCount++;
+ }
+ return true;
+ }
+
+ private boolean calcBitrateAndFrameSize(int code) {
+ int frmsizecode = code >>> 1;
+ int flag = code & 1;
+ if (frmsizecode > 18 || flag > 1 || fscod > 2) {
+ return false;
+ }
+ bitrate = bitRateAndFrameSizeTable[frmsizecode][flag][fscod][0];
+ frameSize = 2 * bitRateAndFrameSizeTable[frmsizecode][flag][fscod][1];
+ return true;
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+ readSamples = true;
+ byte[] header = new byte[5];
+ boolean ret = false;
+ inputStream.mark(5);
+ while (-1 != inputStream.read(header)) {
+ ret = true;
+ int frmsizecode = header[4] & 63;
+ calcBitrateAndFrameSize(frmsizecode);
+ inputStream.reset();
+ byte[] data = new byte[frameSize];
+ inputStream.read(data);
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1536));
+ inputStream.mark(5);
+ }
+ return ret;
+ }
+
+ private void initBitRateAndFrameSizeTable() {
+ // ETSI 102 366 Table 4.13, in frmsizecod, flag, fscod, bitrate/size order. Note that all sizes are in words, and all bitrates in kbps
+
+ // 48kHz
+ bitRateAndFrameSizeTable[0][0][0][0] = 32;
+ bitRateAndFrameSizeTable[0][1][0][0] = 32;
+ bitRateAndFrameSizeTable[0][0][0][1] = 64;
+ bitRateAndFrameSizeTable[0][1][0][1] = 64;
+ bitRateAndFrameSizeTable[1][0][0][0] = 40;
+ bitRateAndFrameSizeTable[1][1][0][0] = 40;
+ bitRateAndFrameSizeTable[1][0][0][1] = 80;
+ bitRateAndFrameSizeTable[1][1][0][1] = 80;
+ bitRateAndFrameSizeTable[2][0][0][0] = 48;
+ bitRateAndFrameSizeTable[2][1][0][0] = 48;
+ bitRateAndFrameSizeTable[2][0][0][1] = 96;
+ bitRateAndFrameSizeTable[2][1][0][1] = 96;
+ bitRateAndFrameSizeTable[3][0][0][0] = 56;
+ bitRateAndFrameSizeTable[3][1][0][0] = 56;
+ bitRateAndFrameSizeTable[3][0][0][1] = 112;
+ bitRateAndFrameSizeTable[3][1][0][1] = 112;
+ bitRateAndFrameSizeTable[4][0][0][0] = 64;
+ bitRateAndFrameSizeTable[4][1][0][0] = 64;
+ bitRateAndFrameSizeTable[4][0][0][1] = 128;
+ bitRateAndFrameSizeTable[4][1][0][1] = 128;
+ bitRateAndFrameSizeTable[5][0][0][0] = 80;
+ bitRateAndFrameSizeTable[5][1][0][0] = 80;
+ bitRateAndFrameSizeTable[5][0][0][1] = 160;
+ bitRateAndFrameSizeTable[5][1][0][1] = 160;
+ bitRateAndFrameSizeTable[6][0][0][0] = 96;
+ bitRateAndFrameSizeTable[6][1][0][0] = 96;
+ bitRateAndFrameSizeTable[6][0][0][1] = 192;
+ bitRateAndFrameSizeTable[6][1][0][1] = 192;
+ bitRateAndFrameSizeTable[7][0][0][0] = 112;
+ bitRateAndFrameSizeTable[7][1][0][0] = 112;
+ bitRateAndFrameSizeTable[7][0][0][1] = 224;
+ bitRateAndFrameSizeTable[7][1][0][1] = 224;
+ bitRateAndFrameSizeTable[8][0][0][0] = 128;
+ bitRateAndFrameSizeTable[8][1][0][0] = 128;
+ bitRateAndFrameSizeTable[8][0][0][1] = 256;
+ bitRateAndFrameSizeTable[8][1][0][1] = 256;
+ bitRateAndFrameSizeTable[9][0][0][0] = 160;
+ bitRateAndFrameSizeTable[9][1][0][0] = 160;
+ bitRateAndFrameSizeTable[9][0][0][1] = 320;
+ bitRateAndFrameSizeTable[9][1][0][1] = 320;
+ bitRateAndFrameSizeTable[10][0][0][0] = 192;
+ bitRateAndFrameSizeTable[10][1][0][0] = 192;
+ bitRateAndFrameSizeTable[10][0][0][1] = 384;
+ bitRateAndFrameSizeTable[10][1][0][1] = 384;
+ bitRateAndFrameSizeTable[11][0][0][0] = 224;
+ bitRateAndFrameSizeTable[11][1][0][0] = 224;
+ bitRateAndFrameSizeTable[11][0][0][1] = 448;
+ bitRateAndFrameSizeTable[11][1][0][1] = 448;
+ bitRateAndFrameSizeTable[12][0][0][0] = 256;
+ bitRateAndFrameSizeTable[12][1][0][0] = 256;
+ bitRateAndFrameSizeTable[12][0][0][1] = 512;
+ bitRateAndFrameSizeTable[12][1][0][1] = 512;
+ bitRateAndFrameSizeTable[13][0][0][0] = 320;
+ bitRateAndFrameSizeTable[13][1][0][0] = 320;
+ bitRateAndFrameSizeTable[13][0][0][1] = 640;
+ bitRateAndFrameSizeTable[13][1][0][1] = 640;
+ bitRateAndFrameSizeTable[14][0][0][0] = 384;
+ bitRateAndFrameSizeTable[14][1][0][0] = 384;
+ bitRateAndFrameSizeTable[14][0][0][1] = 768;
+ bitRateAndFrameSizeTable[14][1][0][1] = 768;
+ bitRateAndFrameSizeTable[15][0][0][0] = 448;
+ bitRateAndFrameSizeTable[15][1][0][0] = 448;
+ bitRateAndFrameSizeTable[15][0][0][1] = 896;
+ bitRateAndFrameSizeTable[15][1][0][1] = 896;
+ bitRateAndFrameSizeTable[16][0][0][0] = 512;
+ bitRateAndFrameSizeTable[16][1][0][0] = 512;
+ bitRateAndFrameSizeTable[16][0][0][1] = 1024;
+ bitRateAndFrameSizeTable[16][1][0][1] = 1024;
+ bitRateAndFrameSizeTable[17][0][0][0] = 576;
+ bitRateAndFrameSizeTable[17][1][0][0] = 576;
+ bitRateAndFrameSizeTable[17][0][0][1] = 1152;
+ bitRateAndFrameSizeTable[17][1][0][1] = 1152;
+ bitRateAndFrameSizeTable[18][0][0][0] = 640;
+ bitRateAndFrameSizeTable[18][1][0][0] = 640;
+ bitRateAndFrameSizeTable[18][0][0][1] = 1280;
+ bitRateAndFrameSizeTable[18][1][0][1] = 1280;
+
+ // 44.1 kHz
+ bitRateAndFrameSizeTable[0][0][1][0] = 32;
+ bitRateAndFrameSizeTable[0][1][1][0] = 32;
+ bitRateAndFrameSizeTable[0][0][1][1] = 69;
+ bitRateAndFrameSizeTable[0][1][1][1] = 70;
+ bitRateAndFrameSizeTable[1][0][1][0] = 40;
+ bitRateAndFrameSizeTable[1][1][1][0] = 40;
+ bitRateAndFrameSizeTable[1][0][1][1] = 87;
+ bitRateAndFrameSizeTable[1][1][1][1] = 88;
+ bitRateAndFrameSizeTable[2][0][1][0] = 48;
+ bitRateAndFrameSizeTable[2][1][1][0] = 48;
+ bitRateAndFrameSizeTable[2][0][1][1] = 104;
+ bitRateAndFrameSizeTable[2][1][1][1] = 105;
+ bitRateAndFrameSizeTable[3][0][1][0] = 56;
+ bitRateAndFrameSizeTable[3][1][1][0] = 56;
+ bitRateAndFrameSizeTable[3][0][1][1] = 121;
+ bitRateAndFrameSizeTable[3][1][1][1] = 122;
+ bitRateAndFrameSizeTable[4][0][1][0] = 64;
+ bitRateAndFrameSizeTable[4][1][1][0] = 64;
+ bitRateAndFrameSizeTable[4][0][1][1] = 139;
+ bitRateAndFrameSizeTable[4][1][1][1] = 140;
+ bitRateAndFrameSizeTable[5][0][1][0] = 80;
+ bitRateAndFrameSizeTable[5][1][1][0] = 80;
+ bitRateAndFrameSizeTable[5][0][1][1] = 174;
+ bitRateAndFrameSizeTable[5][1][1][1] = 175;
+ bitRateAndFrameSizeTable[6][0][1][0] = 96;
+ bitRateAndFrameSizeTable[6][1][1][0] = 96;
+ bitRateAndFrameSizeTable[6][0][1][1] = 208;
+ bitRateAndFrameSizeTable[6][1][1][1] = 209;
+ bitRateAndFrameSizeTable[7][0][1][0] = 112;
+ bitRateAndFrameSizeTable[7][1][1][0] = 112;
+ bitRateAndFrameSizeTable[7][0][1][1] = 243;
+ bitRateAndFrameSizeTable[7][1][1][1] = 244;
+ bitRateAndFrameSizeTable[8][0][1][0] = 128;
+ bitRateAndFrameSizeTable[8][1][1][0] = 128;
+ bitRateAndFrameSizeTable[8][0][1][1] = 278;
+ bitRateAndFrameSizeTable[8][1][1][1] = 279;
+ bitRateAndFrameSizeTable[9][0][1][0] = 160;
+ bitRateAndFrameSizeTable[9][1][1][0] = 160;
+ bitRateAndFrameSizeTable[9][0][1][1] = 348;
+ bitRateAndFrameSizeTable[9][1][1][1] = 349;
+ bitRateAndFrameSizeTable[10][0][1][0] = 192;
+ bitRateAndFrameSizeTable[10][1][1][0] = 192;
+ bitRateAndFrameSizeTable[10][0][1][1] = 417;
+ bitRateAndFrameSizeTable[10][1][1][1] = 418;
+ bitRateAndFrameSizeTable[11][0][1][0] = 224;
+ bitRateAndFrameSizeTable[11][1][1][0] = 224;
+ bitRateAndFrameSizeTable[11][0][1][1] = 487;
+ bitRateAndFrameSizeTable[11][1][1][1] = 488;
+ bitRateAndFrameSizeTable[12][0][1][0] = 256;
+ bitRateAndFrameSizeTable[12][1][1][0] = 256;
+ bitRateAndFrameSizeTable[12][0][1][1] = 557;
+ bitRateAndFrameSizeTable[12][1][1][1] = 558;
+ bitRateAndFrameSizeTable[13][0][1][0] = 320;
+ bitRateAndFrameSizeTable[13][1][1][0] = 320;
+ bitRateAndFrameSizeTable[13][0][1][1] = 696;
+ bitRateAndFrameSizeTable[13][1][1][1] = 697;
+ bitRateAndFrameSizeTable[14][0][1][0] = 384;
+ bitRateAndFrameSizeTable[14][1][1][0] = 384;
+ bitRateAndFrameSizeTable[14][0][1][1] = 835;
+ bitRateAndFrameSizeTable[14][1][1][1] = 836;
+ bitRateAndFrameSizeTable[15][0][1][0] = 448;
+ bitRateAndFrameSizeTable[15][1][1][0] = 448;
+ bitRateAndFrameSizeTable[15][0][1][1] = 975;
+ bitRateAndFrameSizeTable[15][1][1][1] = 975;
+ bitRateAndFrameSizeTable[16][0][1][0] = 512;
+ bitRateAndFrameSizeTable[16][1][1][0] = 512;
+ bitRateAndFrameSizeTable[16][0][1][1] = 1114;
+ bitRateAndFrameSizeTable[16][1][1][1] = 1115;
+ bitRateAndFrameSizeTable[17][0][1][0] = 576;
+ bitRateAndFrameSizeTable[17][1][1][0] = 576;
+ bitRateAndFrameSizeTable[17][0][1][1] = 1253;
+ bitRateAndFrameSizeTable[17][1][1][1] = 1254;
+ bitRateAndFrameSizeTable[18][0][1][0] = 640;
+ bitRateAndFrameSizeTable[18][1][1][0] = 640;
+ bitRateAndFrameSizeTable[18][0][1][1] = 1393;
+ bitRateAndFrameSizeTable[18][1][1][1] = 1394;
+
+ // 32kHz
+ bitRateAndFrameSizeTable[0][0][2][0] = 32;
+ bitRateAndFrameSizeTable[0][1][2][0] = 32;
+ bitRateAndFrameSizeTable[0][0][2][1] = 96;
+ bitRateAndFrameSizeTable[0][1][2][1] = 96;
+ bitRateAndFrameSizeTable[1][0][2][0] = 40;
+ bitRateAndFrameSizeTable[1][1][2][0] = 40;
+ bitRateAndFrameSizeTable[1][0][2][1] = 120;
+ bitRateAndFrameSizeTable[1][1][2][1] = 120;
+ bitRateAndFrameSizeTable[2][0][2][0] = 48;
+ bitRateAndFrameSizeTable[2][1][2][0] = 48;
+ bitRateAndFrameSizeTable[2][0][2][1] = 144;
+ bitRateAndFrameSizeTable[2][1][2][1] = 144;
+ bitRateAndFrameSizeTable[3][0][2][0] = 56;
+ bitRateAndFrameSizeTable[3][1][2][0] = 56;
+ bitRateAndFrameSizeTable[3][0][2][1] = 168;
+ bitRateAndFrameSizeTable[3][1][2][1] = 168;
+ bitRateAndFrameSizeTable[4][0][2][0] = 64;
+ bitRateAndFrameSizeTable[4][1][2][0] = 64;
+ bitRateAndFrameSizeTable[4][0][2][1] = 192;
+ bitRateAndFrameSizeTable[4][1][2][1] = 192;
+ bitRateAndFrameSizeTable[5][0][2][0] = 80;
+ bitRateAndFrameSizeTable[5][1][2][0] = 80;
+ bitRateAndFrameSizeTable[5][0][2][1] = 240;
+ bitRateAndFrameSizeTable[5][1][2][1] = 240;
+ bitRateAndFrameSizeTable[6][0][2][0] = 96;
+ bitRateAndFrameSizeTable[6][1][2][0] = 96;
+ bitRateAndFrameSizeTable[6][0][2][1] = 288;
+ bitRateAndFrameSizeTable[6][1][2][1] = 288;
+ bitRateAndFrameSizeTable[7][0][2][0] = 112;
+ bitRateAndFrameSizeTable[7][1][2][0] = 112;
+ bitRateAndFrameSizeTable[7][0][2][1] = 336;
+ bitRateAndFrameSizeTable[7][1][2][1] = 336;
+ bitRateAndFrameSizeTable[8][0][2][0] = 128;
+ bitRateAndFrameSizeTable[8][1][2][0] = 128;
+ bitRateAndFrameSizeTable[8][0][2][1] = 384;
+ bitRateAndFrameSizeTable[8][1][2][1] = 384;
+ bitRateAndFrameSizeTable[9][0][2][0] = 160;
+ bitRateAndFrameSizeTable[9][1][2][0] = 160;
+ bitRateAndFrameSizeTable[9][0][2][1] = 480;
+ bitRateAndFrameSizeTable[9][1][2][1] = 480;
+ bitRateAndFrameSizeTable[10][0][2][0] = 192;
+ bitRateAndFrameSizeTable[10][1][2][0] = 192;
+ bitRateAndFrameSizeTable[10][0][2][1] = 576;
+ bitRateAndFrameSizeTable[10][1][2][1] = 576;
+ bitRateAndFrameSizeTable[11][0][2][0] = 224;
+ bitRateAndFrameSizeTable[11][1][2][0] = 224;
+ bitRateAndFrameSizeTable[11][0][2][1] = 672;
+ bitRateAndFrameSizeTable[11][1][2][1] = 672;
+ bitRateAndFrameSizeTable[12][0][2][0] = 256;
+ bitRateAndFrameSizeTable[12][1][2][0] = 256;
+ bitRateAndFrameSizeTable[12][0][2][1] = 768;
+ bitRateAndFrameSizeTable[12][1][2][1] = 768;
+ bitRateAndFrameSizeTable[13][0][2][0] = 320;
+ bitRateAndFrameSizeTable[13][1][2][0] = 320;
+ bitRateAndFrameSizeTable[13][0][2][1] = 960;
+ bitRateAndFrameSizeTable[13][1][2][1] = 960;
+ bitRateAndFrameSizeTable[14][0][2][0] = 384;
+ bitRateAndFrameSizeTable[14][1][2][0] = 384;
+ bitRateAndFrameSizeTable[14][0][2][1] = 1152;
+ bitRateAndFrameSizeTable[14][1][2][1] = 1152;
+ bitRateAndFrameSizeTable[15][0][2][0] = 448;
+ bitRateAndFrameSizeTable[15][1][2][0] = 448;
+ bitRateAndFrameSizeTable[15][0][2][1] = 1344;
+ bitRateAndFrameSizeTable[15][1][2][1] = 1344;
+ bitRateAndFrameSizeTable[16][0][2][0] = 512;
+ bitRateAndFrameSizeTable[16][1][2][0] = 512;
+ bitRateAndFrameSizeTable[16][0][2][1] = 1536;
+ bitRateAndFrameSizeTable[16][1][2][1] = 1536;
+ bitRateAndFrameSizeTable[17][0][2][0] = 576;
+ bitRateAndFrameSizeTable[17][1][2][0] = 576;
+ bitRateAndFrameSizeTable[17][0][2][1] = 1728;
+ bitRateAndFrameSizeTable[17][1][2][1] = 1728;
+ bitRateAndFrameSizeTable[18][0][2][0] = 640;
+ bitRateAndFrameSizeTable[18][1][2][0] = 640;
+ bitRateAndFrameSizeTable[18][0][2][1] = 1920;
+ bitRateAndFrameSizeTable[18][1][2][1] = 1920;
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/Amf0Track.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/Amf0Track.java.svn-base
new file mode 100644
index 0000000..0917767
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/Amf0Track.java.svn-base
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.adobe.ActionMessageFormat0SampleEntryBox;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public class Amf0Track extends AbstractTrack {
+ SortedMap<Long, byte[]> rawSamples = new TreeMap<Long, byte[]>() {
+ };
+ private TrackMetaData trackMetaData = new TrackMetaData();
+
+
+ /**
+ * Creates a new AMF0 track from
+ *
+ * @param rawSamples
+ */
+ public Amf0Track(Map<Long, byte[]> rawSamples) {
+ this.rawSamples = new TreeMap<Long, byte[]>(rawSamples);
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000); // Text tracks use millieseconds
+ trackMetaData.setLanguage("eng");
+ }
+
+ public List<ByteBuffer> getSamples() {
+ LinkedList<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ for (byte[] bytes : rawSamples.values()) {
+ samples.add(ByteBuffer.wrap(bytes));
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ SampleDescriptionBox stsd = new SampleDescriptionBox();
+ ActionMessageFormat0SampleEntryBox amf0 = new ActionMessageFormat0SampleEntryBox();
+ amf0.setDataReferenceIndex(1);
+ stsd.addBox(amf0);
+ return stsd;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ LinkedList<TimeToSampleBox.Entry> timesToSample = new LinkedList<TimeToSampleBox.Entry>();
+ LinkedList<Long> keys = new LinkedList<Long>(rawSamples.keySet());
+ Collections.sort(keys);
+ long lastTimeStamp = 0;
+ for (Long key : keys) {
+ long delta = key - lastTimeStamp;
+ if (timesToSample.size() > 0 && timesToSample.peek().getDelta() == delta) {
+ timesToSample.peek().setCount(timesToSample.peek().getCount() + 1);
+ } else {
+ timesToSample.add(new TimeToSampleBox.Entry(1, delta));
+ }
+ lastTimeStamp = key;
+ }
+ return timesToSample;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ // AMF0 tracks do not have Composition Time
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ // AMF0 tracks do not have Sync Samples
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ // AMF0 tracks do not have Sample Dependencies
+ return null;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public String getHandler() {
+ return "data";
+ }
+
+ public Box getMediaHeaderBox() {
+ return new NullMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AppendTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AppendTrack.java.svn-base
new file mode 100644
index 0000000..93ee0cd
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/AppendTrack.java.svn-base
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.DecoderConfigDescriptor;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.util.*;
+
+/**
+ * Appends two or more <code>Tracks</code> of the same type. No only that the type must be equal
+ * also the decoder settings must be the same.
+ */
+public class AppendTrack extends AbstractTrack {
+ Track[] tracks;
+ SampleDescriptionBox stsd;
+
+ public AppendTrack(Track... tracks) throws IOException {
+ this.tracks = tracks;
+
+ for (Track track : tracks) {
+
+ if (stsd == null) {
+ stsd = track.getSampleDescriptionBox();
+ } else {
+ ByteArrayOutputStream curBaos = new ByteArrayOutputStream();
+ ByteArrayOutputStream refBaos = new ByteArrayOutputStream();
+ track.getSampleDescriptionBox().getBox(Channels.newChannel(curBaos));
+ stsd.getBox(Channels.newChannel(refBaos));
+ byte[] cur = curBaos.toByteArray();
+ byte[] ref = refBaos.toByteArray();
+
+ if (!Arrays.equals(ref, cur)) {
+ SampleDescriptionBox curStsd = track.getSampleDescriptionBox();
+ if (stsd.getBoxes().size() == 1 && curStsd.getBoxes().size() == 1) {
+ if (stsd.getBoxes().get(0) instanceof AudioSampleEntry && curStsd.getBoxes().get(0) instanceof AudioSampleEntry) {
+ AudioSampleEntry aseResult = mergeAudioSampleEntries((AudioSampleEntry) stsd.getBoxes().get(0), (AudioSampleEntry) curStsd.getBoxes().get(0));
+ if (aseResult != null) {
+ stsd.setBoxes(Collections.<Box>singletonList(aseResult));
+ return;
+ }
+ }
+ }
+ throw new IOException("Cannot append " + track + " to " + tracks[0] + " since their Sample Description Boxes differ: \n" + track.getSampleDescriptionBox() + "\n vs. \n" + tracks[0].getSampleDescriptionBox());
+ }
+ }
+ }
+ }
+
+ private AudioSampleEntry mergeAudioSampleEntries(AudioSampleEntry ase1, AudioSampleEntry ase2) throws IOException {
+ if (ase1.getType().equals(ase2.getType())) {
+ AudioSampleEntry ase = new AudioSampleEntry(ase2.getType());
+ if (ase1.getBytesPerFrame() == ase2.getBytesPerFrame()) {
+ ase.setBytesPerFrame(ase1.getBytesPerFrame());
+ } else {
+ return null;
+ }
+ if (ase1.getBytesPerPacket() == ase2.getBytesPerPacket()) {
+ ase.setBytesPerPacket(ase1.getBytesPerPacket());
+ } else {
+ return null;
+ }
+ if (ase1.getBytesPerSample() == ase2.getBytesPerSample()) {
+ ase.setBytesPerSample(ase1.getBytesPerSample());
+ } else {
+ return null;
+ }
+ if (ase1.getChannelCount() == ase2.getChannelCount()) {
+ ase.setChannelCount(ase1.getChannelCount());
+ } else {
+ return null;
+ }
+ if (ase1.getPacketSize() == ase2.getPacketSize()) {
+ ase.setPacketSize(ase1.getPacketSize());
+ } else {
+ return null;
+ }
+ if (ase1.getCompressionId() == ase2.getCompressionId()) {
+ ase.setCompressionId(ase1.getCompressionId());
+ } else {
+ return null;
+ }
+ if (ase1.getSampleRate() == ase2.getSampleRate()) {
+ ase.setSampleRate(ase1.getSampleRate());
+ } else {
+ return null;
+ }
+ if (ase1.getSampleSize() == ase2.getSampleSize()) {
+ ase.setSampleSize(ase1.getSampleSize());
+ } else {
+ return null;
+ }
+ if (ase1.getSamplesPerPacket() == ase2.getSamplesPerPacket()) {
+ ase.setSamplesPerPacket(ase1.getSamplesPerPacket());
+ } else {
+ return null;
+ }
+ if (ase1.getSoundVersion() == ase2.getSoundVersion()) {
+ ase.setSoundVersion(ase1.getSoundVersion());
+ } else {
+ return null;
+ }
+ if (Arrays.equals(ase1.getSoundVersion2Data(), ase2.getSoundVersion2Data())) {
+ ase.setSoundVersion2Data(ase1.getSoundVersion2Data());
+ } else {
+ return null;
+ }
+ if (ase1.getBoxes().size() == ase2.getBoxes().size()) {
+ Iterator<Box> bxs1 = ase1.getBoxes().iterator();
+ Iterator<Box> bxs2 = ase2.getBoxes().iterator();
+ while (bxs1.hasNext()) {
+ Box cur1 = bxs1.next();
+ Box cur2 = bxs2.next();
+ ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+ ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+ cur1.getBox(Channels.newChannel(baos1));
+ cur2.getBox(Channels.newChannel(baos2));
+ if (Arrays.equals(baos1.toByteArray(), baos2.toByteArray())) {
+ ase.addBox(cur1);
+ } else {
+ if (ESDescriptorBox.TYPE.equals(cur1.getType()) && ESDescriptorBox.TYPE.equals(cur2.getType())) {
+ ESDescriptorBox esdsBox1 = (ESDescriptorBox) cur1;
+ ESDescriptorBox esdsBox2 = (ESDescriptorBox) cur2;
+ ESDescriptor esds1 = esdsBox1.getEsDescriptor();
+ ESDescriptor esds2 = esdsBox2.getEsDescriptor();
+ if (esds1.getURLFlag() != esds2.getURLFlag()) {
+ return null;
+ }
+ if (esds1.getURLLength() != esds2.getURLLength()) {
+ return null;
+ }
+ if (esds1.getDependsOnEsId() != esds2.getDependsOnEsId()) {
+ return null;
+ }
+ if (esds1.getEsId() != esds2.getEsId()) {
+ return null;
+ }
+ if (esds1.getoCREsId() != esds2.getoCREsId()) {
+ return null;
+ }
+ if (esds1.getoCRstreamFlag() != esds2.getoCRstreamFlag()) {
+ return null;
+ }
+ if (esds1.getRemoteODFlag() != esds2.getRemoteODFlag()) {
+ return null;
+ }
+ if (esds1.getStreamDependenceFlag() != esds2.getStreamDependenceFlag()) {
+ return null;
+ }
+ if (esds1.getStreamPriority() != esds2.getStreamPriority()) {
+ return null;
+ }
+ if (esds1.getURLString() != null ? !esds1.getURLString().equals(esds2.getURLString()) : esds2.getURLString() != null) {
+ return null;
+ }
+ if (esds1.getDecoderConfigDescriptor() != null ? !esds1.getDecoderConfigDescriptor().equals(esds2.getDecoderConfigDescriptor()) : esds2.getDecoderConfigDescriptor() != null) {
+ DecoderConfigDescriptor dcd1 = esds1.getDecoderConfigDescriptor();
+ DecoderConfigDescriptor dcd2 = esds2.getDecoderConfigDescriptor();
+ if (!dcd1.getAudioSpecificInfo().equals(dcd2.getAudioSpecificInfo())) {
+ return null;
+ }
+ if (dcd1.getAvgBitRate() != dcd2.getAvgBitRate()) {
+ // I don't care
+ }
+ if (dcd1.getBufferSizeDB() != dcd2.getBufferSizeDB()) {
+ // I don't care
+ }
+
+ if (dcd1.getDecoderSpecificInfo() != null ? !dcd1.getDecoderSpecificInfo().equals(dcd2.getDecoderSpecificInfo()) : dcd2.getDecoderSpecificInfo() != null) {
+ return null;
+ }
+
+ if (dcd1.getMaxBitRate() != dcd2.getMaxBitRate()) {
+ // I don't care
+ }
+ if (!dcd1.getProfileLevelIndicationDescriptors().equals(dcd2.getProfileLevelIndicationDescriptors())) {
+ return null;
+ }
+
+ if (dcd1.getObjectTypeIndication() != dcd2.getObjectTypeIndication()) {
+ return null;
+ }
+ if (dcd1.getStreamType() != dcd2.getStreamType()) {
+ return null;
+ }
+ if (dcd1.getUpStream() != dcd2.getUpStream()) {
+ return null;
+ }
+
+
+ }
+ if (esds1.getOtherDescriptors() != null ? !esds1.getOtherDescriptors().equals(esds2.getOtherDescriptors()) : esds2.getOtherDescriptors() != null) {
+ return null;
+ }
+ if (esds1.getSlConfigDescriptor() != null ? !esds1.getSlConfigDescriptor().equals(esds2.getSlConfigDescriptor()) : esds2.getSlConfigDescriptor() != null) {
+ return null;
+ }
+ ase.addBox(cur1);
+ }
+ }
+ }
+ }
+ return ase;
+ } else {
+ return null;
+ }
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ ArrayList<ByteBuffer> lists = new ArrayList<ByteBuffer>();
+
+ for (Track track : tracks) {
+ lists.addAll(track.getSamples());
+ }
+
+ return lists;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return stsd;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ if (tracks[0].getDecodingTimeEntries() != null && !tracks[0].getDecodingTimeEntries().isEmpty()) {
+ List<long[]> lists = new LinkedList<long[]>();
+ for (Track track : tracks) {
+ lists.add(TimeToSampleBox.blowupTimeToSamples(track.getDecodingTimeEntries()));
+ }
+
+ LinkedList<TimeToSampleBox.Entry> returnDecodingEntries = new LinkedList<TimeToSampleBox.Entry>();
+ for (long[] list : lists) {
+ for (long nuDecodingTime : list) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getDelta() != nuDecodingTime) {
+ TimeToSampleBox.Entry e = new TimeToSampleBox.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ TimeToSampleBox.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ if (tracks[0].getCompositionTimeEntries() != null && !tracks[0].getCompositionTimeEntries().isEmpty()) {
+ List<int[]> lists = new LinkedList<int[]>();
+ for (Track track : tracks) {
+ lists.add(CompositionTimeToSample.blowupCompositionTimes(track.getCompositionTimeEntries()));
+ }
+ LinkedList<CompositionTimeToSample.Entry> compositionTimeEntries = new LinkedList<CompositionTimeToSample.Entry>();
+ for (int[] list : lists) {
+ for (int compositionTime : list) {
+ if (compositionTimeEntries.isEmpty() || compositionTimeEntries.getLast().getOffset() != compositionTime) {
+ CompositionTimeToSample.Entry e = new CompositionTimeToSample.Entry(1, compositionTime);
+ compositionTimeEntries.add(e);
+ } else {
+ CompositionTimeToSample.Entry e = compositionTimeEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ }
+ return compositionTimeEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public long[] getSyncSamples() {
+ if (tracks[0].getSyncSamples() != null && tracks[0].getSyncSamples().length > 0) {
+ int numSyncSamples = 0;
+ for (Track track : tracks) {
+ numSyncSamples += track.getSyncSamples().length;
+ }
+ long[] returnSyncSamples = new long[numSyncSamples];
+
+ int pos = 0;
+ long samplesBefore = 0;
+ for (Track track : tracks) {
+ for (long l : track.getSyncSamples()) {
+ returnSyncSamples[pos++] = samplesBefore + l;
+ }
+ samplesBefore += track.getSamples().size();
+ }
+ return returnSyncSamples;
+ } else {
+ return null;
+ }
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ if (tracks[0].getSampleDependencies() != null && !tracks[0].getSampleDependencies().isEmpty()) {
+ List<SampleDependencyTypeBox.Entry> list = new LinkedList<SampleDependencyTypeBox.Entry>();
+ for (Track track : tracks) {
+ list.addAll(track.getSampleDependencies());
+ }
+ return list;
+ } else {
+ return null;
+ }
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return tracks[0].getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return tracks[0].getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return tracks[0].getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return tracks[0].getSubsampleInformationBox();
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ChangeTimeScaleTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ChangeTimeScaleTrack.java.svn-base
new file mode 100644
index 0000000..50f76c2
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ChangeTimeScaleTrack.java.svn-base
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.logging.Logger;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class ChangeTimeScaleTrack implements Track {
+ private static final Logger LOG = Logger.getLogger(ChangeTimeScaleTrack.class.getName());
+
+ Track source;
+ List<CompositionTimeToSample.Entry> ctts;
+ List<TimeToSampleBox.Entry> tts;
+ long timeScale;
+
+ /**
+ * Changes the time scale of the source track to the target time scale and makes sure
+ * that any rounding errors that may have summed are corrected exactly before the syncSamples.
+ *
+ * @param source the source track
+ * @param targetTimeScale the resulting time scale of this track.
+ * @param syncSamples at these sync points where rounding error are corrected.
+ */
+ public ChangeTimeScaleTrack(Track source, long targetTimeScale, long[] syncSamples) {
+ this.source = source;
+ this.timeScale = targetTimeScale;
+ double timeScaleFactor = (double) targetTimeScale / source.getTrackMetaData().getTimescale();
+ ctts = adjustCtts(source.getCompositionTimeEntries(), timeScaleFactor);
+ tts = adjustTts(source.getDecodingTimeEntries(), timeScaleFactor, syncSamples, getTimes(source, syncSamples, targetTimeScale));
+ }
+
+ private static long[] getTimes(Track track, long[] syncSamples, long targetTimeScale) {
+ long[] syncSampleTimes = new long[syncSamples.length];
+ Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries());
+
+ int currentSample = 1; // first syncsample is 1
+ long currentDuration = 0;
+ long currentDelta = 0;
+ int currentSyncSampleIndex = 0;
+ long left = 0;
+
+
+ while (currentSample <= syncSamples[syncSamples.length - 1]) {
+ if (currentSample++ == syncSamples[currentSyncSampleIndex]) {
+ syncSampleTimes[currentSyncSampleIndex++] = (currentDuration * targetTimeScale) / track.getTrackMetaData().getTimescale();
+ }
+ if (left-- == 0) {
+ TimeToSampleBox.Entry entry = timeQueue.poll();
+ left = entry.getCount() - 1;
+ currentDelta = entry.getDelta();
+ }
+ currentDuration += currentDelta;
+ }
+ return syncSampleTimes;
+
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return tts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return ctts;
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(timeScale);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ /**
+ * Adjusting the composition times is easy. Just scale it by the factor - that's it. There is no rounding
+ * error summing up.
+ *
+ * @param source
+ * @param timeScaleFactor
+ * @return
+ */
+ static List<CompositionTimeToSample.Entry> adjustCtts(List<CompositionTimeToSample.Entry> source, double timeScaleFactor) {
+ if (source != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(source.size());
+ for (CompositionTimeToSample.Entry entry : source) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), (int) Math.round(timeScaleFactor * entry.getOffset())));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ static List<TimeToSampleBox.Entry> adjustTts(List<TimeToSampleBox.Entry> source, double timeScaleFactor, long[] syncSample, long[] syncSampleTimes) {
+
+ long[] sourceArray = TimeToSampleBox.blowupTimeToSamples(source);
+ long summedDurations = 0;
+
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (int i = 1; i <= sourceArray.length; i++) {
+ long duration = sourceArray[i - 1];
+
+ long x = Math.round(timeScaleFactor * duration);
+
+
+ TimeToSampleBox.Entry last = entries2.peekLast();
+ int ssIndex;
+ if ((ssIndex = Arrays.binarySearch(syncSample, i + 1)) >= 0) {
+ // we are at the sample before sync point
+ if (syncSampleTimes[ssIndex] != summedDurations) {
+ long correction = syncSampleTimes[ssIndex] - (summedDurations + x);
+ LOG.finest(String.format("Sample %d %d / %d - correct by %d", i, summedDurations, syncSampleTimes[ssIndex], correction));
+ x += correction;
+ }
+ }
+ summedDurations += x;
+ if (last == null) {
+ entries2.add(new TimeToSampleBox.Entry(1, x));
+ } else if (last.getDelta() != x) {
+ entries2.add(new TimeToSampleBox.Entry(1, x));
+ } else {
+ last.setCount(last.getCount() + 1);
+ }
+
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "ChangeTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/CroppedTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/CroppedTrack.java.svn-base
new file mode 100644
index 0000000..2389961
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/CroppedTrack.java.svn-base
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Generates a Track that starts at fromSample and ends at toSample (exclusive). The user of this class
+ * has to make sure that the fromSample is a random access sample.
+ * <ul>
+ * <li>In AAC this is every single sample</li>
+ * <li>In H264 this is every sample that is marked in the SyncSampleBox</li>
+ * </ul>
+ */
+public class CroppedTrack extends AbstractTrack {
+ Track origTrack;
+ private int fromSample;
+ private int toSample;
+ private long[] syncSampleArray;
+
+ public CroppedTrack(Track origTrack, long fromSample, long toSample) {
+ this.origTrack = origTrack;
+ assert fromSample <= Integer.MAX_VALUE;
+ assert toSample <= Integer.MAX_VALUE;
+ this.fromSample = (int) fromSample;
+ this.toSample = (int) toSample;
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return origTrack.getSamples().subList(fromSample, toSample);
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return origTrack.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ if (origTrack.getDecodingTimeEntries() != null && !origTrack.getDecodingTimeEntries().isEmpty()) {
+ // todo optimize! too much long is allocated but then not used
+ long[] decodingTimes = TimeToSampleBox.blowupTimeToSamples(origTrack.getDecodingTimeEntries());
+ long[] nuDecodingTimes = new long[toSample - fromSample];
+ System.arraycopy(decodingTimes, fromSample, nuDecodingTimes, 0, toSample - fromSample);
+
+ LinkedList<TimeToSampleBox.Entry> returnDecodingEntries = new LinkedList<TimeToSampleBox.Entry>();
+
+ for (long nuDecodingTime : nuDecodingTimes) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getDelta() != nuDecodingTime) {
+ TimeToSampleBox.Entry e = new TimeToSampleBox.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ TimeToSampleBox.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ if (origTrack.getCompositionTimeEntries() != null && !origTrack.getCompositionTimeEntries().isEmpty()) {
+ int[] compositionTime = CompositionTimeToSample.blowupCompositionTimes(origTrack.getCompositionTimeEntries());
+ int[] nuCompositionTimes = new int[toSample - fromSample];
+ System.arraycopy(compositionTime, fromSample, nuCompositionTimes, 0, toSample - fromSample);
+
+ LinkedList<CompositionTimeToSample.Entry> returnDecodingEntries = new LinkedList<CompositionTimeToSample.Entry>();
+
+ for (int nuDecodingTime : nuCompositionTimes) {
+ if (returnDecodingEntries.isEmpty() || returnDecodingEntries.getLast().getOffset() != nuDecodingTime) {
+ CompositionTimeToSample.Entry e = new CompositionTimeToSample.Entry(1, nuDecodingTime);
+ returnDecodingEntries.add(e);
+ } else {
+ CompositionTimeToSample.Entry e = returnDecodingEntries.getLast();
+ e.setCount(e.getCount() + 1);
+ }
+ }
+ return returnDecodingEntries;
+ } else {
+ return null;
+ }
+ }
+
+ synchronized public long[] getSyncSamples() {
+ if (this.syncSampleArray == null) {
+ if (origTrack.getSyncSamples() != null && origTrack.getSyncSamples().length > 0) {
+ List<Long> syncSamples = new LinkedList<Long>();
+ for (long l : origTrack.getSyncSamples()) {
+ if (l >= fromSample && l < toSample) {
+ syncSamples.add(l - fromSample);
+ }
+ }
+ syncSampleArray = new long[syncSamples.size()];
+ for (int i = 0; i < syncSampleArray.length; i++) {
+ syncSampleArray[i] = syncSamples.get(i);
+
+ }
+ return syncSampleArray;
+ } else {
+ return null;
+ }
+ } else {
+ return this.syncSampleArray;
+ }
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ if (origTrack.getSampleDependencies() != null && !origTrack.getSampleDependencies().isEmpty()) {
+ return origTrack.getSampleDependencies().subList(fromSample, toSample);
+ } else {
+ return null;
+ }
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return origTrack.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return origTrack.getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return origTrack.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return origTrack.getSubsampleInformationBox();
+ }
+
+} \ No newline at end of file
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/DivideTimeScaleTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/DivideTimeScaleTrack.java.svn-base
new file mode 100644
index 0000000..c51e8e0
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/DivideTimeScaleTrack.java.svn-base
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class DivideTimeScaleTrack implements Track {
+ Track source;
+ private int timeScaleDivisor;
+
+ public DivideTimeScaleTrack(Track source, int timeScaleDivisor) {
+ this.source = source;
+ this.timeScaleDivisor = timeScaleDivisor;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return adjustTts();
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return adjustCtts();
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(source.getTrackMetaData().getTimescale() / this.timeScaleDivisor);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ List<CompositionTimeToSample.Entry> adjustCtts() {
+ List<CompositionTimeToSample.Entry> origCtts = this.source.getCompositionTimeEntries();
+ if (origCtts != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(origCtts.size());
+ for (CompositionTimeToSample.Entry entry : origCtts) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), entry.getOffset() / timeScaleDivisor));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ List<TimeToSampleBox.Entry> adjustTts() {
+ List<TimeToSampleBox.Entry> origTts = source.getDecodingTimeEntries();
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (TimeToSampleBox.Entry e : origTts) {
+ entries2.add(new TimeToSampleBox.Entry(e.getCount(), e.getDelta() / timeScaleDivisor));
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "MultiplyTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/EC3TrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/EC3TrackImpl.java.svn-base
new file mode 100644
index 0000000..d0b2d76
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/EC3TrackImpl.java.svn-base
@@ -0,0 +1,436 @@
+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.EC3SpecificBox;
+import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: magnus
+ * Date: 2012-03-14
+ * Time: 10:39
+ * To change this template use File | Settings | File Templates.
+ */
+public class EC3TrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ int samplerate;
+ int bitrate;
+ int frameSize;
+
+ List<BitStreamInfo> entries = new LinkedList<BitStreamInfo>();
+
+ private BufferedInputStream inputStream;
+ private List<ByteBuffer> samples;
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ private String lang = "und";
+
+ public EC3TrackImpl(InputStream fin, String lang) throws IOException {
+ this.lang = lang;
+ parse(fin);
+ }
+
+ public EC3TrackImpl(InputStream fin) throws IOException {
+ parse(fin);
+ }
+
+ private void parse(InputStream fin) throws IOException {
+ inputStream = new BufferedInputStream(fin);
+
+ boolean done = false;
+ inputStream.mark(10000);
+ while (!done) {
+ BitStreamInfo bsi = readVariables();
+ if (bsi == null) {
+ throw new IOException();
+ }
+ for (BitStreamInfo entry : entries) {
+ if (bsi.strmtyp != 1 && entry.substreamid == bsi.substreamid) {
+ done = true;
+ }
+ }
+ if (!done) {
+ entries.add(bsi);
+ long skipped = inputStream.skip(bsi.frameSize);
+ assert skipped == bsi.frameSize;
+ }
+ }
+
+ inputStream.reset();
+
+ if (entries.size() == 0) {
+ throw new IOException();
+ }
+ samplerate = entries.get(0).samplerate;
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ec-3");
+ audioSampleEntry.setChannelCount(2); // According to ETSI TS 102 366 Annex F
+ audioSampleEntry.setSampleRate(samplerate);
+ audioSampleEntry.setDataReferenceIndex(1);
+ audioSampleEntry.setSampleSize(16);
+
+ EC3SpecificBox ec3 = new EC3SpecificBox();
+ int[] deps = new int[entries.size()];
+ int[] chan_locs = new int[entries.size()];
+ for (BitStreamInfo bsi : entries) {
+ if (bsi.strmtyp == 1) {
+ deps[bsi.substreamid]++;
+ chan_locs[bsi.substreamid] = ((bsi.chanmap >> 6) & 0x100) | ((bsi.chanmap >> 5) & 0xff);
+ }
+ }
+ for (BitStreamInfo bsi : entries) {
+ if (bsi.strmtyp != 1) {
+ EC3SpecificBox.Entry e = new EC3SpecificBox.Entry();
+ e.fscod = bsi.fscod;
+ e.bsid = bsi.bsid;
+ e.bsmod = bsi.bsmod;
+ e.acmod = bsi.acmod;
+ e.lfeon = bsi.lfeon;
+ e.reserved = 0;
+ e.num_dep_sub = deps[bsi.substreamid];
+ e.chan_loc = chan_locs[bsi.substreamid];
+ e.reserved2 = 0;
+ ec3.addEntry(e);
+ }
+ bitrate += bsi.bitrate;
+ frameSize += bsi.frameSize;
+ }
+
+ ec3.setDataRate(bitrate / 1000);
+ audioSampleEntry.addBox(ec3);
+ sampleDescriptionBox.addBox(audioSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+
+ return samples;
+ }
+
+ 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 AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new SoundMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private BitStreamInfo readVariables() throws IOException {
+ byte[] data = new byte[200];
+ inputStream.mark(200);
+ if (200 != inputStream.read(data, 0, 200)) {
+ return null;
+ }
+ inputStream.reset(); // Rewind
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ BitReaderBuffer brb = new BitReaderBuffer(bb);
+ int syncword = brb.readBits(16);
+ if (syncword != 0xb77) {
+ return null;
+ }
+
+ BitStreamInfo entry = new BitStreamInfo();
+
+ entry.strmtyp = brb.readBits(2);
+ entry.substreamid = brb.readBits(3);
+ int frmsiz = brb.readBits(11);
+ entry.frameSize = 2 * (frmsiz + 1);
+
+ entry.fscod = brb.readBits(2);
+ int fscod2 = -1;
+ int numblkscod;
+ if (entry.fscod == 3) {
+ fscod2 = brb.readBits(2);
+ numblkscod = 3;
+ } else {
+ numblkscod = brb.readBits(2);
+ }
+ int numberOfBlocksPerSyncFrame = 0;
+ switch (numblkscod) {
+ case 0:
+ numberOfBlocksPerSyncFrame = 1;
+ break;
+
+ case 1:
+ numberOfBlocksPerSyncFrame = 2;
+ break;
+
+ case 2:
+ numberOfBlocksPerSyncFrame = 3;
+ break;
+
+ case 3:
+ numberOfBlocksPerSyncFrame = 6;
+ break;
+
+ }
+ entry.frameSize *= (6 / numberOfBlocksPerSyncFrame);
+
+ entry.acmod = brb.readBits(3);
+ entry.lfeon = brb.readBits(1);
+ entry.bsid = brb.readBits(5);
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8); // compr
+ }
+ if (0 == entry.acmod) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8);
+ }
+ }
+ if (1 == entry.strmtyp) {
+ if (1 == brb.readBits(1)) {
+ entry.chanmap = brb.readBits(16);
+ }
+ }
+ if (1 == brb.readBits(1)) { // mixing metadata
+ if (entry.acmod > 2) {
+ brb.readBits(2);
+ }
+ if (1 == (entry.acmod & 1) && entry.acmod > 2) {
+ brb.readBits(3);
+ brb.readBits(3);
+ }
+ if (0 < (entry.acmod & 4)) {
+ brb.readBits(3);
+ brb.readBits(3);
+ }
+ if (1 == entry.lfeon) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ }
+ }
+ if (0 == entry.strmtyp) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ if (0 == entry.acmod) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(6);
+ }
+ int mixdef = brb.readBits(2);
+ if (1 == mixdef) {
+ brb.readBits(5);
+ } else if (2 == mixdef) {
+ brb.readBits(12);
+ } else if (3 == mixdef) {
+ int mixdeflen = brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(4);
+ }
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(7);
+ if (1 == brb.readBits(1)) {
+ brb.readBits(8);
+ }
+ }
+ }
+ for (int i = 0; i < (mixdeflen + 2); i++) {
+ brb.readBits(8);
+ }
+ brb.byteSync();
+ }
+ if (entry.acmod < 2) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(14);
+ }
+ if (0 == entry.acmod) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(14);
+ }
+ }
+ if (1 == brb.readBits(1)) {
+ if (numblkscod == 0) {
+ brb.readBits(5);
+ } else {
+ for (int i = 0; i < numberOfBlocksPerSyncFrame; i++) {
+ if (1 == brb.readBits(1)) {
+ brb.readBits(5);
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ if (1 == brb.readBits(1)) { // infomdate
+ entry.bsmod = brb.readBits(3);
+ }
+
+ switch (entry.fscod) {
+ case 0:
+ entry.samplerate = 48000;
+ break;
+
+ case 1:
+ entry.samplerate = 44100;
+ break;
+
+ case 2:
+ entry.samplerate = 32000;
+ break;
+
+ case 3: {
+ switch (fscod2) {
+ case 0:
+ entry.samplerate = 24000;
+ break;
+
+ case 1:
+ entry.samplerate = 22050;
+ break;
+
+ case 2:
+ entry.samplerate = 16000;
+ break;
+
+ case 3:
+ entry.samplerate = 0;
+ break;
+ }
+ break;
+ }
+
+ }
+ if (entry.samplerate == 0) {
+ return null;
+ }
+
+ entry.bitrate = (int) (((double) entry.samplerate) / 1536.0 * entry.frameSize * 8);
+
+ return entry;
+ }
+
+ private boolean readSamples() throws IOException {
+ int read = frameSize;
+ boolean ret = false;
+ while (frameSize == read) {
+ ret = true;
+ byte[] data = new byte[frameSize];
+ read = inputStream.read(data);
+ if (read == frameSize) {
+ samples.add(ByteBuffer.wrap(data));
+ stts.add(new TimeToSampleBox.Entry(1, 1536));
+ }
+ }
+ return ret;
+ }
+
+ public static class BitStreamInfo extends EC3SpecificBox.Entry {
+ public int frameSize;
+ public int substreamid;
+ public int bitrate;
+ public int samplerate;
+ public int strmtyp;
+ public int chanmap;
+
+ @Override
+ public String toString() {
+ return "BitStreamInfo{" +
+ "frameSize=" + frameSize +
+ ", substreamid=" + substreamid +
+ ", bitrate=" + bitrate +
+ ", samplerate=" + samplerate +
+ ", strmtyp=" + strmtyp +
+ ", chanmap=" + chanmap +
+ '}';
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "EC3TrackImpl{" +
+ "bitrate=" + bitrate +
+ ", samplerate=" + samplerate +
+ ", entries=" + entries +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/H264TrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/H264TrackImpl.java.svn-base
new file mode 100644
index 0000000..b3c1866
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/H264TrackImpl.java.svn-base
@@ -0,0 +1,740 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
+import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.h264.model.PictureParameterSet;
+import com.googlecode.mp4parser.h264.model.SeqParameterSet;
+import com.googlecode.mp4parser.h264.read.CAVLCReader;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * The <code>H264TrackImpl</code> creates a <code>Track</code> from an H.264
+ * Annex B file.
+ */
+public class H264TrackImpl extends AbstractTrack {
+ private static final Logger LOG = Logger.getLogger(H264TrackImpl.class.getName());
+
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+
+ private ReaderWrapper reader;
+ private List<ByteBuffer> samples;
+ boolean readSamples = false;
+
+ List<TimeToSampleBox.Entry> stts;
+ List<CompositionTimeToSample.Entry> ctts;
+ List<SampleDependencyTypeBox.Entry> sdtp;
+ List<Integer> stss;
+
+ SeqParameterSet seqParameterSet = null;
+ PictureParameterSet pictureParameterSet = null;
+ LinkedList<byte[]> seqParameterSetList = new LinkedList<byte[]>();
+ LinkedList<byte[]> pictureParameterSetList = new LinkedList<byte[]>();
+
+ private int width;
+ private int height;
+ private int timescale;
+ private int frametick;
+ private int currentScSize;
+ private int prevScSize;
+
+ private SEIMessage seiMessage;
+ int frameNrInGop = 0;
+ private boolean determineFrameRate = true;
+ private String lang = "und";
+
+ public H264TrackImpl(InputStream inputStream, String lang, long timescale) throws IOException {
+ this.lang = lang;
+ if (timescale > 1000) {
+ timescale = timescale; //e.g. 23976
+ frametick = 1000;
+ determineFrameRate = false;
+ } else {
+ throw new IllegalArgumentException("Timescale must be specified in milliseconds!");
+ }
+ parse(inputStream);
+ }
+
+ public H264TrackImpl(InputStream inputStream, String lang) throws IOException {
+ this.lang = lang;
+ parse(inputStream);
+ }
+
+ public H264TrackImpl(InputStream inputStream) throws IOException {
+ parse(inputStream);
+ }
+
+ private void parse(InputStream inputStream) throws IOException {
+ this.reader = new ReaderWrapper(inputStream);
+ stts = new LinkedList<TimeToSampleBox.Entry>();
+ ctts = new LinkedList<CompositionTimeToSample.Entry>();
+ sdtp = new LinkedList<SampleDependencyTypeBox.Entry>();
+ stss = new LinkedList<Integer>();
+
+ samples = new LinkedList<ByteBuffer>();
+ if (!readSamples()) {
+ throw new IOException();
+ }
+
+ if (!readVariables()) {
+ throw new IOException();
+ }
+
+ sampleDescriptionBox = new SampleDescriptionBox();
+ VisualSampleEntry visualSampleEntry = new VisualSampleEntry("avc1");
+ visualSampleEntry.setDataReferenceIndex(1);
+ visualSampleEntry.setDepth(24);
+ visualSampleEntry.setFrameCount(1);
+ visualSampleEntry.setHorizresolution(72);
+ visualSampleEntry.setVertresolution(72);
+ visualSampleEntry.setWidth(width);
+ visualSampleEntry.setHeight(height);
+ visualSampleEntry.setCompressorname("AVC Coding");
+
+ AvcConfigurationBox avcConfigurationBox = new AvcConfigurationBox();
+
+ avcConfigurationBox.setSequenceParameterSets(seqParameterSetList);
+ avcConfigurationBox.setPictureParameterSets(pictureParameterSetList);
+ avcConfigurationBox.setAvcLevelIndication(seqParameterSet.level_idc);
+ avcConfigurationBox.setAvcProfileIndication(seqParameterSet.profile_idc);
+ avcConfigurationBox.setBitDepthLumaMinus8(seqParameterSet.bit_depth_luma_minus8);
+ avcConfigurationBox.setBitDepthChromaMinus8(seqParameterSet.bit_depth_chroma_minus8);
+ avcConfigurationBox.setChromaFormat(seqParameterSet.chroma_format_idc.getId());
+ avcConfigurationBox.setConfigurationVersion(1);
+ avcConfigurationBox.setLengthSizeMinusOne(3);
+ avcConfigurationBox.setProfileCompatibility(seqParameterSetList.get(0)[1]);
+
+ visualSampleEntry.addBox(avcConfigurationBox);
+ sampleDescriptionBox.addBox(visualSampleEntry);
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setLanguage(lang);
+ trackMetaData.setTimescale(timescale);
+ trackMetaData.setWidth(width);
+ trackMetaData.setHeight(height);
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return stts;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return ctts;
+ }
+
+ public long[] getSyncSamples() {
+ long[] returns = new long[stss.size()];
+ for (int i = 0; i < stss.size(); i++) {
+ returns[i] = stss.get(i);
+ }
+ return returns;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return sdtp;
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return "vide";
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new VideoMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ private boolean readVariables() {
+ width = (seqParameterSet.pic_width_in_mbs_minus1 + 1) * 16;
+ int mult = 2;
+ if (seqParameterSet.frame_mbs_only_flag) {
+ mult = 1;
+ }
+ height = 16 * (seqParameterSet.pic_height_in_map_units_minus1 + 1) * mult;
+ if (seqParameterSet.frame_cropping_flag) {
+ int chromaArrayType = 0;
+ if (seqParameterSet.residual_color_transform_flag == false) {
+ chromaArrayType = seqParameterSet.chroma_format_idc.getId();
+ }
+ int cropUnitX = 1;
+ int cropUnitY = mult;
+ if (chromaArrayType != 0) {
+ cropUnitX = seqParameterSet.chroma_format_idc.getSubWidth();
+ cropUnitY = seqParameterSet.chroma_format_idc.getSubHeight() * mult;
+ }
+
+ width -= cropUnitX * (seqParameterSet.frame_crop_left_offset + seqParameterSet.frame_crop_right_offset);
+ height -= cropUnitY * (seqParameterSet.frame_crop_top_offset + seqParameterSet.frame_crop_bottom_offset);
+ }
+ return true;
+ }
+
+ private boolean findNextStartcode() throws IOException {
+ byte[] test = new byte[]{-1, -1, -1, -1};
+
+ int c;
+ while ((c = reader.read()) != -1) {
+ test[0] = test[1];
+ test[1] = test[2];
+ test[2] = test[3];
+ test[3] = (byte) c;
+ if (test[0] == 0 && test[1] == 0 && test[2] == 0 && test[3] == 1) {
+ prevScSize = currentScSize;
+ currentScSize = 4;
+ return true;
+ }
+ if (test[0] == 0 && test[1] == 0 && test[2] == 1) {
+ prevScSize = currentScSize;
+ currentScSize = 3;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private enum NALActions {
+ IGNORE, BUFFER, STORE, END
+ }
+
+ private boolean readSamples() throws IOException {
+ if (readSamples) {
+ return true;
+ }
+
+ readSamples = true;
+
+
+ findNextStartcode();
+ reader.mark();
+ long pos = reader.getPos();
+
+ ArrayList<byte[]> buffered = new ArrayList<byte[]>();
+
+ int frameNr = 0;
+
+ while (findNextStartcode()) {
+ long newpos = reader.getPos();
+ int size = (int) (newpos - pos - prevScSize);
+ reader.reset();
+ byte[] data = new byte[size ];
+ reader.read(data);
+ int type = data[0];
+ int nal_ref_idc = (type >> 5) & 3;
+ int nal_unit_type = type & 0x1f;
+ LOG.fine("Found startcode at " + (pos -4) + " Type: " + nal_unit_type + " ref idc: " + nal_ref_idc + " (size " + size + ")");
+ NALActions action = handleNALUnit(nal_ref_idc, nal_unit_type, data);
+ switch (action) {
+ case IGNORE:
+ break;
+
+ case BUFFER:
+ buffered.add(data);
+ break;
+
+ case STORE:
+ int stdpValue = 22;
+ frameNr++;
+ buffered.add(data);
+ ByteBuffer bb = createSample(buffered);
+ boolean IdrPicFlag = false;
+ if (nal_unit_type == 5) {
+ stdpValue += 16;
+ IdrPicFlag = true;
+ }
+ ByteArrayInputStream bs = cleanBuffer(buffered.get(buffered.size() - 1));
+ SliceHeader sh = new SliceHeader(bs, seqParameterSet, pictureParameterSet, IdrPicFlag);
+ if (sh.slice_type == SliceHeader.SliceType.B) {
+ stdpValue += 4;
+ }
+ LOG.fine("Adding sample with size " + bb.capacity() + " and header " + sh);
+ buffered.clear();
+ samples.add(bb);
+ stts.add(new TimeToSampleBox.Entry(1, frametick));
+ if (nal_unit_type == 5) { // IDR Picture
+ stss.add(frameNr);
+ }
+ if (seiMessage.n_frames == 0) {
+ frameNrInGop = 0;
+ }
+ int offset = 0;
+ if (seiMessage.clock_timestamp_flag) {
+ offset = seiMessage.n_frames - frameNrInGop;
+ } else if (seiMessage.removal_delay_flag) {
+ offset = seiMessage.dpb_removal_delay / 2;
+ }
+ ctts.add(new CompositionTimeToSample.Entry(1, offset * frametick));
+ sdtp.add(new SampleDependencyTypeBox.Entry(stdpValue));
+ frameNrInGop++;
+ break;
+
+ case END:
+ return true;
+
+
+ }
+ pos = newpos;
+ reader.seek(currentScSize);
+ reader.mark();
+ }
+ return true;
+ }
+
+ private ByteBuffer createSample(List<byte[]> buffers) {
+ int outsize = 0;
+ for (int i = 0; i < buffers.size(); i++) {
+ outsize += buffers.get(i).length + 4;
+ }
+ byte[] output = new byte[outsize];
+
+ ByteBuffer bb = ByteBuffer.wrap(output);
+ for (int i = 0; i < buffers.size(); i++) {
+ bb.putInt(buffers.get(i).length);
+ bb.put(buffers.get(i));
+ }
+ bb.rewind();
+ return bb;
+ }
+
+ private ByteArrayInputStream cleanBuffer(byte[] data) {
+ byte[] output = new byte[data.length];
+ int inPos = 0;
+ int outPos = 0;
+ while (inPos < data.length) {
+ if (data[inPos] == 0 && data[inPos + 1] == 0 && data[inPos + 2] == 3) {
+ output[outPos] = 0;
+ output[outPos + 1] = 0;
+ inPos += 3;
+ outPos += 2;
+ } else {
+ output[outPos] = data[inPos];
+ inPos++;
+ outPos++;
+ }
+ }
+ return new ByteArrayInputStream(output, 0, outPos);
+ }
+
+ private NALActions handleNALUnit(int nal_ref_idc, int nal_unit_type, byte[] data) throws IOException {
+ NALActions action;
+ switch (nal_unit_type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ action = NALActions.STORE; // Will only work in single slice per frame mode!
+ break;
+
+ case 6:
+ seiMessage = new SEIMessage(cleanBuffer(data), seqParameterSet);
+ action = NALActions.BUFFER;
+ break;
+
+ case 9:
+// printAccessUnitDelimiter(data);
+ int type = data[1] >> 5;
+ LOG.fine("Access unit delimiter type: " + type);
+ action = NALActions.BUFFER;
+ break;
+
+
+ case 7:
+ if (seqParameterSet == null) {
+ ByteArrayInputStream is = cleanBuffer(data);
+ is.read();
+ seqParameterSet = SeqParameterSet.read(is);
+ seqParameterSetList.add(data);
+ configureFramerate();
+ }
+ action = NALActions.IGNORE;
+ break;
+
+ case 8:
+ if (pictureParameterSet == null) {
+ ByteArrayInputStream is = new ByteArrayInputStream(data);
+ is.read();
+ pictureParameterSet = PictureParameterSet.read(is);
+ pictureParameterSetList.add(data);
+ }
+ action = NALActions.IGNORE;
+ break;
+
+ case 10:
+ case 11:
+ action = NALActions.END;
+ break;
+
+ default:
+ System.err.println("Unknown NAL unit type: " + nal_unit_type);
+ action = NALActions.IGNORE;
+
+ }
+
+ return action;
+ }
+
+ private void configureFramerate() {
+ if (determineFrameRate) {
+ if (seqParameterSet.vuiParams != null) {
+ timescale = seqParameterSet.vuiParams.time_scale >> 1; // Not sure why, but I found this in several places, and it works...
+ frametick = seqParameterSet.vuiParams.num_units_in_tick;
+ if (timescale == 0 || frametick == 0) {
+ System.err.println("Warning: vuiParams contain invalid values: time_scale: " + timescale + " and frame_tick: " + frametick + ". Setting frame rate to 25fps");
+ timescale = 90000;
+ frametick = 3600;
+ }
+ } else {
+ System.err.println("Warning: Can't determine frame rate. Guessing 25 fps");
+ timescale = 90000;
+ frametick = 3600;
+ }
+ }
+ }
+
+ public void printAccessUnitDelimiter(byte[] data) {
+ LOG.fine("Access unit delimiter: " + (data[1] >> 5));
+ }
+
+ public static class SliceHeader {
+
+ public enum SliceType {
+ P, B, I, SP, SI
+ }
+
+ public int first_mb_in_slice;
+ public SliceType slice_type;
+ public int pic_parameter_set_id;
+ public int colour_plane_id;
+ public int frame_num;
+ public boolean field_pic_flag = false;
+ public boolean bottom_field_flag = false;
+ public int idr_pic_id;
+ public int pic_order_cnt_lsb;
+ public int delta_pic_order_cnt_bottom;
+
+ public SliceHeader(InputStream is, SeqParameterSet sps, PictureParameterSet pps, boolean IdrPicFlag) throws IOException {
+ is.read();
+ CAVLCReader reader = new CAVLCReader(is);
+ first_mb_in_slice = reader.readUE("SliceHeader: first_mb_in_slice");
+ switch (reader.readUE("SliceHeader: slice_type")) {
+ case 0:
+ case 5:
+ slice_type = SliceType.P;
+ break;
+
+ case 1:
+ case 6:
+ slice_type = SliceType.B;
+ break;
+
+ case 2:
+ case 7:
+ slice_type = SliceType.I;
+ break;
+
+ case 3:
+ case 8:
+ slice_type = SliceType.SP;
+ break;
+
+ case 4:
+ case 9:
+ slice_type = SliceType.SI;
+ break;
+
+ }
+ pic_parameter_set_id = reader.readUE("SliceHeader: pic_parameter_set_id");
+ if (sps.residual_color_transform_flag) {
+ colour_plane_id = reader.readU(2, "SliceHeader: colour_plane_id");
+ }
+ frame_num = reader.readU(sps.log2_max_frame_num_minus4 + 4, "SliceHeader: frame_num");
+
+ if (!sps.frame_mbs_only_flag) {
+ field_pic_flag = reader.readBool("SliceHeader: field_pic_flag");
+ if (field_pic_flag) {
+ bottom_field_flag = reader.readBool("SliceHeader: bottom_field_flag");
+ }
+ }
+ if (IdrPicFlag) {
+ idr_pic_id = reader.readUE("SliceHeader: idr_pic_id");
+ if (sps.pic_order_cnt_type == 0) {
+ pic_order_cnt_lsb = reader.readU(sps.log2_max_pic_order_cnt_lsb_minus4 + 4, "SliceHeader: pic_order_cnt_lsb");
+ if (pps.pic_order_present_flag && !field_pic_flag) {
+ delta_pic_order_cnt_bottom = reader.readSE("SliceHeader: delta_pic_order_cnt_bottom");
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SliceHeader{" +
+ "first_mb_in_slice=" + first_mb_in_slice +
+ ", slice_type=" + slice_type +
+ ", pic_parameter_set_id=" + pic_parameter_set_id +
+ ", colour_plane_id=" + colour_plane_id +
+ ", frame_num=" + frame_num +
+ ", field_pic_flag=" + field_pic_flag +
+ ", bottom_field_flag=" + bottom_field_flag +
+ ", idr_pic_id=" + idr_pic_id +
+ ", pic_order_cnt_lsb=" + pic_order_cnt_lsb +
+ ", delta_pic_order_cnt_bottom=" + delta_pic_order_cnt_bottom +
+ '}';
+ }
+ }
+
+ private class ReaderWrapper {
+ private InputStream inputStream;
+ private long pos = 0;
+
+ private long markPos = 0;
+
+
+ private ReaderWrapper(InputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ int read() throws IOException {
+ pos++;
+ return inputStream.read();
+ }
+
+ long read(byte[] data) throws IOException {
+ long read = inputStream.read(data);
+ pos += read;
+ return read;
+ }
+
+ long seek(int dist) throws IOException {
+ long seeked = inputStream.skip(dist);
+ pos += seeked;
+ return seeked;
+ }
+
+ public long getPos() {
+ return pos;
+ }
+
+ public void mark() {
+ int i = 1048576;
+ LOG.fine("Marking with " + i + " at " + pos);
+ inputStream.mark(i);
+ markPos = pos;
+ }
+
+
+ public void reset() throws IOException {
+ long diff = pos - markPos;
+ LOG.fine("Resetting to " + markPos + " (pos is " + pos + ") which makes the buffersize " + diff);
+ inputStream.reset();
+ pos = markPos;
+ }
+ }
+
+ public class SEIMessage {
+
+ int payloadType = 0;
+ int payloadSize = 0;
+
+ boolean removal_delay_flag;
+ int cpb_removal_delay;
+ int dpb_removal_delay;
+
+ boolean clock_timestamp_flag;
+ int pic_struct;
+ int ct_type;
+ int nuit_field_based_flag;
+ int counting_type;
+ int full_timestamp_flag;
+ int discontinuity_flag;
+ int cnt_dropped_flag;
+ int n_frames;
+ int seconds_value;
+ int minutes_value;
+ int hours_value;
+ int time_offset_length;
+ int time_offset;
+
+ SeqParameterSet sps;
+
+ public SEIMessage(InputStream is, SeqParameterSet sps) throws IOException {
+ this.sps = sps;
+ is.read();
+ int datasize = is.available();
+ int read = 0;
+ while (read < datasize) {
+ payloadType = 0;
+ payloadSize = 0;
+ int last_payload_type_bytes = is.read();
+ read++;
+ while (last_payload_type_bytes == 0xff) {
+ payloadType += last_payload_type_bytes;
+ last_payload_type_bytes = is.read();
+ read++;
+ }
+ payloadType += last_payload_type_bytes;
+ int last_payload_size_bytes = is.read();
+ read++;
+
+ while (last_payload_size_bytes == 0xff) {
+ payloadSize += last_payload_size_bytes;
+ last_payload_size_bytes = is.read();
+ read++;
+ }
+ payloadSize += last_payload_size_bytes;
+ if (datasize - read >= payloadSize) {
+ if (payloadType == 1) { // pic_timing is what we are interested in!
+ if (sps.vuiParams != null && (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null || sps.vuiParams.pic_struct_present_flag)) {
+ byte[] data = new byte[payloadSize];
+ is.read(data);
+ read += payloadSize;
+ CAVLCReader reader = new CAVLCReader(new ByteArrayInputStream(data));
+ if (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null) {
+ removal_delay_flag = true;
+ cpb_removal_delay = reader.readU(sps.vuiParams.nalHRDParams.cpb_removal_delay_length_minus1 + 1, "SEI: cpb_removal_delay");
+ dpb_removal_delay = reader.readU(sps.vuiParams.nalHRDParams.dpb_output_delay_length_minus1 + 1, "SEI: dpb_removal_delay");
+ } else {
+ removal_delay_flag = false;
+ }
+ if (sps.vuiParams.pic_struct_present_flag) {
+ pic_struct = reader.readU(4, "SEI: pic_struct");
+ int numClockTS;
+ switch (pic_struct) {
+ case 0:
+ case 1:
+ case 2:
+ default:
+ numClockTS = 1;
+ break;
+
+ case 3:
+ case 4:
+ case 7:
+ numClockTS = 2;
+ break;
+
+ case 5:
+ case 6:
+ case 8:
+ numClockTS = 3;
+ break;
+ }
+ for (int i = 0; i < numClockTS; i++) {
+ clock_timestamp_flag = reader.readBool("pic_timing SEI: clock_timestamp_flag[" + i + "]");
+ if (clock_timestamp_flag) {
+ ct_type = reader.readU(2, "pic_timing SEI: ct_type");
+ nuit_field_based_flag = reader.readU(1, "pic_timing SEI: nuit_field_based_flag");
+ counting_type = reader.readU(5, "pic_timing SEI: counting_type");
+ full_timestamp_flag = reader.readU(1, "pic_timing SEI: full_timestamp_flag");
+ discontinuity_flag = reader.readU(1, "pic_timing SEI: discontinuity_flag");
+ cnt_dropped_flag = reader.readU(1, "pic_timing SEI: cnt_dropped_flag");
+ n_frames = reader.readU(8, "pic_timing SEI: n_frames");
+ if (full_timestamp_flag == 1) {
+ seconds_value = reader.readU(6, "pic_timing SEI: seconds_value");
+ minutes_value = reader.readU(6, "pic_timing SEI: minutes_value");
+ hours_value = reader.readU(5, "pic_timing SEI: hours_value");
+ } else {
+ if (reader.readBool("pic_timing SEI: seconds_flag")) {
+ seconds_value = reader.readU(6, "pic_timing SEI: seconds_value");
+ if (reader.readBool("pic_timing SEI: minutes_flag")) {
+ minutes_value = reader.readU(6, "pic_timing SEI: minutes_value");
+ if (reader.readBool("pic_timing SEI: hours_flag")) {
+ hours_value = reader.readU(5, "pic_timing SEI: hours_value");
+ }
+ }
+ }
+ }
+ if (true) {
+ if (sps.vuiParams.nalHRDParams != null) {
+ time_offset_length = sps.vuiParams.nalHRDParams.time_offset_length;
+ } else if (sps.vuiParams.vclHRDParams != null) {
+ time_offset_length = sps.vuiParams.vclHRDParams.time_offset_length;
+ } else {
+ time_offset_length = 24;
+ }
+ time_offset = reader.readU(24, "pic_timing SEI: time_offset");
+ }
+ }
+ }
+ }
+
+ } else {
+ for (int i = 0; i < payloadSize; i++) {
+ is.read();
+ read++;
+ }
+ }
+ } else {
+ for (int i = 0; i < payloadSize; i++) {
+ is.read();
+ read++;
+ }
+ }
+ } else {
+ read = datasize;
+ }
+ LOG.fine(this.toString());
+ }
+ }
+
+ @Override
+ public String toString() {
+ String out = "SEIMessage{" +
+ "payloadType=" + payloadType +
+ ", payloadSize=" + payloadSize;
+ if (payloadType == 1) {
+ if (sps.vuiParams.nalHRDParams != null || sps.vuiParams.vclHRDParams != null) {
+
+ out += ", cpb_removal_delay=" + cpb_removal_delay +
+ ", dpb_removal_delay=" + dpb_removal_delay;
+ }
+ if (sps.vuiParams.pic_struct_present_flag) {
+ out += ", pic_struct=" + pic_struct;
+ if (clock_timestamp_flag) {
+ out += ", ct_type=" + ct_type +
+ ", nuit_field_based_flag=" + nuit_field_based_flag +
+ ", counting_type=" + counting_type +
+ ", full_timestamp_flag=" + full_timestamp_flag +
+ ", discontinuity_flag=" + discontinuity_flag +
+ ", cnt_dropped_flag=" + cnt_dropped_flag +
+ ", n_frames=" + n_frames +
+ ", seconds_value=" + seconds_value +
+ ", minutes_value=" + minutes_value +
+ ", hours_value=" + hours_value +
+ ", time_offset_length=" + time_offset_length +
+ ", time_offset=" + time_offset;
+ }
+ }
+ }
+ out += '}';
+ return out;
+ }
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/MultiplyTimeScaleTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/MultiplyTimeScaleTrack.java.svn-base
new file mode 100644
index 0000000..e9a90e4
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/MultiplyTimeScaleTrack.java.svn-base
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.googlecode.mp4parser.util.CastUtils.l2i;
+import static com.googlecode.mp4parser.util.Math.gcd;
+import static com.googlecode.mp4parser.util.Math.lcm;
+import static java.lang.Math.round;
+
+/**
+ * Changes the timescale of a track by wrapping the track.
+ */
+public class MultiplyTimeScaleTrack implements Track {
+ Track source;
+ private int timeScaleFactor;
+
+ public MultiplyTimeScaleTrack(Track source, int timeScaleFactor) {
+ this.source = source;
+ this.timeScaleFactor = timeScaleFactor;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return adjustTts(source.getDecodingTimeEntries(), timeScaleFactor);
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return adjustCtts(source.getCompositionTimeEntries(), timeScaleFactor);
+ }
+
+ public long[] getSyncSamples() {
+ return source.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return source.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ TrackMetaData trackMetaData = (TrackMetaData) source.getTrackMetaData().clone();
+ trackMetaData.setTimescale(source.getTrackMetaData().getTimescale() * this.timeScaleFactor);
+ return trackMetaData;
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return source.getSamples();
+ }
+
+
+ static List<CompositionTimeToSample.Entry> adjustCtts(List<CompositionTimeToSample.Entry> source, int timeScaleFactor) {
+ if (source != null) {
+ List<CompositionTimeToSample.Entry> entries2 = new ArrayList<CompositionTimeToSample.Entry>(source.size());
+ for (CompositionTimeToSample.Entry entry : source) {
+ entries2.add(new CompositionTimeToSample.Entry(entry.getCount(), timeScaleFactor * entry.getOffset()));
+ }
+ return entries2;
+ } else {
+ return null;
+ }
+ }
+
+ static List<TimeToSampleBox.Entry> adjustTts(List<TimeToSampleBox.Entry> source, int timeScaleFactor) {
+ LinkedList<TimeToSampleBox.Entry> entries2 = new LinkedList<TimeToSampleBox.Entry>();
+ for (TimeToSampleBox.Entry e : source) {
+ entries2.add(new TimeToSampleBox.Entry(e.getCount(), timeScaleFactor * e.getDelta()));
+ }
+ return entries2;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return source.getSubsampleInformationBox();
+ }
+
+ @Override
+ public String toString() {
+ return "MultiplyTimeScaleTrack{" +
+ "source=" + source +
+ '}';
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/QuicktimeTextTrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/QuicktimeTextTrackImpl.java.svn-base
new file mode 100644
index 0000000..8efa399
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/QuicktimeTextTrackImpl.java.svn-base
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.TextSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.apple.BaseMediaInfoAtom;
+import com.googlecode.mp4parser.boxes.apple.GenericMediaHeaderAtom;
+import com.googlecode.mp4parser.boxes.apple.GenericMediaHeaderTextAtom;
+import com.googlecode.mp4parser.boxes.apple.QuicktimeTextSampleEntry;
+import com.googlecode.mp4parser.boxes.threegpp26245.FontTableBox;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A Text track as Quicktime Pro would create.
+ */
+public class QuicktimeTextTrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+ List<Line> subs = new LinkedList<Line>();
+
+ public List<Line> getSubs() {
+ return subs;
+ }
+
+ public QuicktimeTextTrackImpl() {
+ sampleDescriptionBox = new SampleDescriptionBox();
+ QuicktimeTextSampleEntry textTrack = new QuicktimeTextSampleEntry();
+ textTrack.setDataReferenceIndex(1);
+ sampleDescriptionBox.addBox(textTrack);
+
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000);
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ samples.add(ByteBuffer.wrap(new byte[]{0, 0}));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ try {
+ dos.writeShort(sub.text.getBytes("UTF-8").length);
+ dos.write(sub.text.getBytes("UTF-8"));
+ dos.close();
+ } catch (IOException e) {
+ throw new Error("VM is broken. Does not support UTF-8");
+ }
+ samples.add(ByteBuffer.wrap(baos.toByteArray()));
+ lastEnd = sub.to;
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ stts.add(new TimeToSampleBox.Entry(1, silentTime));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ stts.add(new TimeToSampleBox.Entry(1, sub.to - sub.from));
+ lastEnd = sub.to;
+ }
+ 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 "text";
+ }
+
+
+ public static class Line {
+ long from;
+ long to;
+ String text;
+
+
+ public Line(long from, long to, String text) {
+ this.from = from;
+ this.to = to;
+ this.text = text;
+ }
+
+ public long getFrom() {
+ return from;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public long getTo() {
+ return to;
+ }
+ }
+
+ public Box getMediaHeaderBox() {
+ GenericMediaHeaderAtom ghmd = new GenericMediaHeaderAtom();
+ ghmd.addBox(new BaseMediaInfoAtom());
+ ghmd.addBox(new GenericMediaHeaderTextAtom());
+ return ghmd;
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ReplaceSampleTrack.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ReplaceSampleTrack.java.svn-base
new file mode 100644
index 0000000..81a129d
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/ReplaceSampleTrack.java.svn-base
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.AbstractList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Generates a Track where a single sample has been replaced by a given <code>ByteBuffer</code>.
+ */
+
+public class ReplaceSampleTrack extends AbstractTrack {
+ Track origTrack;
+ private long sampleNumber;
+ private ByteBuffer sampleContent;
+ private List<ByteBuffer> samples;
+
+ public ReplaceSampleTrack(Track origTrack, long sampleNumber, ByteBuffer content) {
+ this.origTrack = origTrack;
+ this.sampleNumber = sampleNumber;
+ this.sampleContent = content;
+ this.samples = new ReplaceASingleEntryList();
+
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return origTrack.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return origTrack.getDecodingTimeEntries();
+
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return origTrack.getCompositionTimeEntries();
+
+ }
+
+ synchronized public long[] getSyncSamples() {
+ return origTrack.getSyncSamples();
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return origTrack.getSampleDependencies();
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return origTrack.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return origTrack.getHandler();
+ }
+
+ public Box getMediaHeaderBox() {
+ return origTrack.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return origTrack.getSubsampleInformationBox();
+ }
+
+ private class ReplaceASingleEntryList extends AbstractList<ByteBuffer> {
+ @Override
+ public ByteBuffer get(int index) {
+ if (ReplaceSampleTrack.this.sampleNumber == index) {
+ return ReplaceSampleTrack.this.sampleContent;
+ } else {
+ return ReplaceSampleTrack.this.origTrack.getSamples().get(index);
+ }
+ }
+
+ @Override
+ public int size() {
+ return ReplaceSampleTrack.this.origTrack.getSamples().size();
+ }
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/SilenceTrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/SilenceTrackImpl.java.svn-base
new file mode 100644
index 0000000..f74ab3c
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/SilenceTrackImpl.java.svn-base
@@ -0,0 +1,98 @@
+package com.googlecode.mp4parser.authoring.tracks;
+
+import com.coremedia.iso.boxes.*;
+import com.googlecode.mp4parser.authoring.Mp4TrackImpl;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This is just a basic idea how things could work but they don't.
+ */
+public class SilenceTrackImpl implements Track {
+ Track source;
+
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ TimeToSampleBox.Entry entry;
+
+ public SilenceTrackImpl(Track ofType, long ms) {
+ source = ofType;
+ if ("mp4a".equals(ofType.getSampleDescriptionBox().getSampleEntry().getType())) {
+ long numFrames = getTrackMetaData().getTimescale() * ms / 1000 / 1024;
+ long standZeit = getTrackMetaData().getTimescale() * ms / numFrames / 1000;
+ entry = new TimeToSampleBox.Entry(numFrames, standZeit);
+
+
+ while (numFrames-- > 0) {
+ samples.add((ByteBuffer) ByteBuffer.wrap(new byte[]{
+ 0x21, 0x10, 0x04, 0x60, (byte) 0x8c, 0x1c,
+ }).rewind());
+ }
+
+ } else {
+ throw new RuntimeException("Tracks of type " + ofType.getClass().getSimpleName() + " are not supported");
+ }
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return source.getSampleDescriptionBox();
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ return Collections.singletonList(entry);
+
+ }
+
+ public TrackMetaData getTrackMetaData() {
+ return source.getTrackMetaData();
+ }
+
+ public String getHandler() {
+ return source.getHandler();
+ }
+
+ public boolean isEnabled() {
+ return source.isEnabled();
+ }
+
+ public boolean isInMovie() {
+ return source.isInMovie();
+ }
+
+ public boolean isInPreview() {
+ return source.isInPreview();
+ }
+
+ public boolean isInPoster() {
+ return source.isInPoster();
+ }
+
+ public List<ByteBuffer> getSamples() {
+ return samples;
+ }
+
+ public Box getMediaHeaderBox() {
+ return source.getMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+
+ public List<CompositionTimeToSample.Entry> getCompositionTimeEntries() {
+ return null;
+ }
+
+ public long[] getSyncSamples() {
+ return null;
+ }
+
+ public List<SampleDependencyTypeBox.Entry> getSampleDependencies() {
+ return null;
+ }
+
+}
diff --git a/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/TextTrackImpl.java.svn-base b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/TextTrackImpl.java.svn-base
new file mode 100644
index 0000000..3bae143
--- /dev/null
+++ b/isoparser/src/main/java/com/googlecode/mp4parser/authoring/tracks/.svn/text-base/TextTrackImpl.java.svn-base
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012 Sebastian Annies, Hamburg
+ *
+ * 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.TextSampleEntry;
+import com.googlecode.mp4parser.authoring.AbstractTrack;
+import com.googlecode.mp4parser.authoring.TrackMetaData;
+import com.googlecode.mp4parser.boxes.threegpp26245.FontTableBox;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class TextTrackImpl extends AbstractTrack {
+ TrackMetaData trackMetaData = new TrackMetaData();
+ SampleDescriptionBox sampleDescriptionBox;
+ List<Line> subs = new LinkedList<Line>();
+
+ public List<Line> getSubs() {
+ return subs;
+ }
+
+ public TextTrackImpl() {
+ sampleDescriptionBox = new SampleDescriptionBox();
+ TextSampleEntry tx3g = new TextSampleEntry("tx3g");
+ tx3g.setDataReferenceIndex(1);
+ tx3g.setStyleRecord(new TextSampleEntry.StyleRecord());
+ tx3g.setBoxRecord(new TextSampleEntry.BoxRecord());
+ sampleDescriptionBox.addBox(tx3g);
+
+ FontTableBox ftab = new FontTableBox();
+ ftab.setEntries(Collections.singletonList(new FontTableBox.FontRecord(1, "Serif")));
+
+ tx3g.addBox(ftab);
+
+
+ trackMetaData.setCreationTime(new Date());
+ trackMetaData.setModificationTime(new Date());
+ trackMetaData.setTimescale(1000); // Text tracks use millieseconds
+
+
+ }
+
+
+ public List<ByteBuffer> getSamples() {
+ List<ByteBuffer> samples = new LinkedList<ByteBuffer>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ samples.add(ByteBuffer.wrap(new byte[]{0, 0}));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ try {
+ dos.writeShort(sub.text.getBytes("UTF-8").length);
+ dos.write(sub.text.getBytes("UTF-8"));
+ dos.close();
+ } catch (IOException e) {
+ throw new Error("VM is broken. Does not support UTF-8");
+ }
+ samples.add(ByteBuffer.wrap(baos.toByteArray()));
+ lastEnd = sub.to;
+ }
+ return samples;
+ }
+
+ public SampleDescriptionBox getSampleDescriptionBox() {
+ return sampleDescriptionBox;
+ }
+
+ public List<TimeToSampleBox.Entry> getDecodingTimeEntries() {
+ List<TimeToSampleBox.Entry> stts = new LinkedList<TimeToSampleBox.Entry>();
+ long lastEnd = 0;
+ for (Line sub : subs) {
+ long silentTime = sub.from - lastEnd;
+ if (silentTime > 0) {
+ stts.add(new TimeToSampleBox.Entry(1, silentTime));
+ } else if (silentTime < 0) {
+ throw new Error("Subtitle display times may not intersect");
+ }
+ stts.add(new TimeToSampleBox.Entry(1, sub.to - sub.from));
+ lastEnd = sub.to;
+ }
+ 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 "sbtl";
+ }
+
+
+ public static class Line {
+ long from;
+ long to;
+ String text;
+
+
+ public Line(long from, long to, String text) {
+ this.from = from;
+ this.to = to;
+ this.text = text;
+ }
+
+ public long getFrom() {
+ return from;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public long getTo() {
+ return to;
+ }
+ }
+
+ public AbstractMediaHeaderBox getMediaHeaderBox() {
+ return new NullMediaHeaderBox();
+ }
+
+ public SubSampleInformationBox getSubsampleInformationBox() {
+ return null;
+ }
+}