1/*
2 * Copyright 2008 CoreMedia AG, Hamburg
3 *
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.coremedia.iso.boxes;
18
19
20import com.coremedia.iso.IsoTypeReader;
21import com.coremedia.iso.IsoTypeWriter;
22import com.googlecode.mp4parser.AbstractFullBox;
23
24import java.nio.ByteBuffer;
25import java.util.ArrayList;
26import java.util.Collections;
27import java.util.List;
28
29import static com.googlecode.mp4parser.util.CastUtils.l2i;
30
31/**
32 * This box contains a compact version of a table that allows indexing from decoding time to sample number.
33 * Other tables give sample sizes and pointers, from the sample number. Each entry in the table gives the
34 * number of consecutive samples with the same time delta, and the delta of those samples. By adding the
35 * deltas a complete time-to-sample map may be built.<br>
36 * The Decoding Time to Sample Box contains decode time delta's: <code>DT(n+1) = DT(n) + STTS(n)</code> where STTS(n)
37 * is the (uncompressed) table entry for sample n.<br>
38 * The sample entries are ordered by decoding time stamps; therefore the deltas are all non-negative. <br>
39 * The DT axis has a zero origin; <code>DT(i) = SUM(for j=0 to i-1 of delta(j))</code>, and the sum of all
40 * deltas gives the length of the media in the track (not mapped to the overall timescale, and not considering
41 * any edit list).    <br>
42 * The Edit List Box provides the initial CT value if it is non-empty (non-zero).
43 */
44public class TimeToSampleBox extends AbstractFullBox {
45    public static final String TYPE = "stts";
46
47    List<Entry> entries = Collections.emptyList();
48
49
50    public TimeToSampleBox() {
51        super(TYPE);
52    }
53
54    protected long getContentSize() {
55        return 8 + entries.size() * 8;
56    }
57
58    @Override
59    public void _parseDetails(ByteBuffer content) {
60        parseVersionAndFlags(content);
61        int entryCount = l2i(IsoTypeReader.readUInt32(content));
62        entries = new ArrayList<Entry>(entryCount);
63
64        for (int i = 0; i < entryCount; i++) {
65            entries.add(new Entry(IsoTypeReader.readUInt32(content), IsoTypeReader.readUInt32(content)));
66        }
67
68    }
69
70    @Override
71    protected void getContent(ByteBuffer byteBuffer) {
72        writeVersionAndFlags(byteBuffer);
73        IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
74        for (Entry entry : entries) {
75            IsoTypeWriter.writeUInt32(byteBuffer, entry.getCount());
76            IsoTypeWriter.writeUInt32(byteBuffer, entry.getDelta());
77        }
78    }
79
80    public List<Entry> getEntries() {
81        return entries;
82    }
83
84    public void setEntries(List<Entry> entries) {
85        this.entries = entries;
86    }
87
88    public String toString() {
89        return "TimeToSampleBox[entryCount=" + entries.size() + "]";
90    }
91
92    public static class Entry {
93        long count;
94        long delta;
95
96        public Entry(long count, long delta) {
97            this.count = count;
98            this.delta = delta;
99        }
100
101        public long getCount() {
102            return count;
103        }
104
105        public long getDelta() {
106            return delta;
107        }
108
109        public void setCount(long count) {
110            this.count = count;
111        }
112
113        public void setDelta(long delta) {
114            this.delta = delta;
115        }
116
117        @Override
118        public String toString() {
119            return "Entry{" +
120                    "count=" + count +
121                    ", delta=" + delta +
122                    '}';
123        }
124    }
125
126    /**
127     * Decompresses the list of entries and returns the list of decoding times.
128     *
129     * @return decoding time per sample
130     */
131    public static long[] blowupTimeToSamples(List<TimeToSampleBox.Entry> entries) {
132        long numOfSamples = 0;
133        for (TimeToSampleBox.Entry entry : entries) {
134            numOfSamples += entry.getCount();
135        }
136        assert numOfSamples <= Integer.MAX_VALUE;
137        long[] decodingTime = new long[(int) numOfSamples];
138
139        int current = 0;
140
141
142        for (TimeToSampleBox.Entry entry : entries) {
143            for (int i = 0; i < entry.getCount(); i++) {
144                decodingTime[current++] = entry.getDelta();
145            }
146        }
147
148        return decodingTime;
149    }
150
151
152}
153