CompositionTimeToSample.java revision dd9eb897ee7c7b507cbdcf80263bb4b5de6966bf
1package com.coremedia.iso.boxes;
2
3import com.coremedia.iso.IsoTypeReader;
4import com.coremedia.iso.IsoTypeWriter;
5import com.googlecode.mp4parser.AbstractFullBox;
6
7import java.nio.ByteBuffer;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.List;
11
12import static com.googlecode.mp4parser.util.CastUtils.l2i;
13
14/**
15 * <pre>
16 * aligned(8) class CompositionOffsetBox
17 * extends FullBox(‘ctts’, version = 0, 0) {
18 *  unsigned int(32) entry_count;
19 *  int i;
20 *  if (version==0) {
21 *   for (i=0; i < entry_count; i++) {
22 *    unsigned int(32) sample_count;
23 *    unsigned int(32) sample_offset;
24 *   }
25 *  }
26 *  else if (version == 1) {
27 *   for (i=0; i < entry_count; i++) {
28 *    unsigned int(32) sample_count;
29 *    signed int(32) sample_offset;
30 *   }
31 *  }
32 * }
33 * </pre>
34 * <p/>
35 * This box provides the offset between decoding time and composition time.
36 * In version 0 of this box the decoding time must be less than the composition time, and
37 * the offsets are expressed as unsigned numbers such that
38 * CT(n) = DT(n) + CTTS(n) where CTTS(n) is the (uncompressed) table entry for sample n.
39 * <p/>
40 * In version 1 of this box, the composition timeline and the decoding timeline are
41 * still derived from each other, but the offsets are signed.
42 * It is recommended that for the computed composition timestamps, there is
43 * exactly one with the value 0 (zero).
44 */
45public class CompositionTimeToSample extends AbstractFullBox {
46    public static final String TYPE = "ctts";
47
48    List<Entry> entries = Collections.emptyList();
49
50    public CompositionTimeToSample() {
51        super(TYPE);
52    }
53
54    protected long getContentSize() {
55        return 8 + 8 * entries.size();
56    }
57
58    public List<Entry> getEntries() {
59        return entries;
60    }
61
62    public void setEntries(List<Entry> entries) {
63        this.entries = entries;
64    }
65
66    @Override
67    public void _parseDetails(ByteBuffer content) {
68        parseVersionAndFlags(content);
69        int numberOfEntries = l2i(IsoTypeReader.readUInt32(content));
70        entries = new ArrayList<Entry>(numberOfEntries);
71        for (int i = 0; i < numberOfEntries; i++) {
72            Entry e = new Entry(l2i(IsoTypeReader.readUInt32(content)), content.getInt());
73            entries.add(e);
74        }
75    }
76
77    @Override
78    protected void getContent(ByteBuffer byteBuffer) {
79        writeVersionAndFlags(byteBuffer);
80        IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
81
82        for (Entry entry : entries) {
83            IsoTypeWriter.writeUInt32(byteBuffer, entry.getCount());
84            byteBuffer.putInt(entry.getOffset());
85        }
86
87    }
88
89
90    public static class Entry {
91        int count;
92        int offset;
93
94        public Entry(int count, int offset) {
95            this.count = count;
96            this.offset = offset;
97        }
98
99        public int getCount() {
100            return count;
101        }
102
103        public int getOffset() {
104            return offset;
105        }
106
107        public void setCount(int count) {
108            this.count = count;
109        }
110
111        public void setOffset(int offset) {
112            this.offset = offset;
113        }
114
115        @Override
116        public String toString() {
117            return "Entry{" +
118                    "count=" + count +
119                    ", offset=" + offset +
120                    '}';
121        }
122    }
123
124
125    /**
126     * Decompresses the list of entries and returns the list of composition times.
127     *
128     * @return decoding time per sample
129     */
130    public static int[] blowupCompositionTimes(List<CompositionTimeToSample.Entry> entries) {
131        long numOfSamples = 0;
132        for (CompositionTimeToSample.Entry entry : entries) {
133            numOfSamples += entry.getCount();
134        }
135        assert numOfSamples <= Integer.MAX_VALUE;
136        int[] decodingTime = new int[(int) numOfSamples];
137
138        int current = 0;
139
140
141        for (CompositionTimeToSample.Entry entry : entries) {
142            for (int i = 0; i < entry.getCount(); i++) {
143                decodingTime[current++] = entry.getOffset();
144            }
145        }
146
147        return decodingTime;
148    }
149
150}
151