TrackRunBox.java revision dd9eb897ee7c7b507cbdcf80263bb4b5de6966bf
1/*
2 * Copyright 2009 castLabs GmbH, Berlin
3 *
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.coremedia.iso.boxes.fragment;
18
19import com.coremedia.iso.IsoTypeReader;
20import com.coremedia.iso.IsoTypeWriter;
21import com.coremedia.iso.boxes.MovieBox;
22import com.googlecode.mp4parser.AbstractFullBox;
23
24import java.nio.ByteBuffer;
25import java.util.ArrayList;
26import java.util.List;
27
28import static com.googlecode.mp4parser.util.CastUtils.l2i;
29
30/**
31 * aligned(8) class TrackRunBox
32 * extends FullBox('trun', 0, tr_flags) {
33 * unsigned int(32) sample_count;
34 * // the following are optional fields
35 * signed int(32) data_offset;
36 * unsigned int(32) first_sample_flags;
37 * // all fields in the following array are optional
38 * {
39 * unsigned int(32) sample_duration;
40 * unsigned int(32) sample_size;
41 * unsigned int(32) sample_flags
42 * unsigned int(32) sample_composition_time_offset;
43 * }[ sample_count ]
44 * }
45 */
46
47public class TrackRunBox extends AbstractFullBox {
48    public static final String TYPE = "trun";
49    private int dataOffset;
50    private SampleFlags firstSampleFlags;
51    private List<Entry> entries = new ArrayList<Entry>();
52
53
54    public List<Entry> getEntries() {
55        return entries;
56    }
57
58    public static class Entry {
59        private long sampleDuration;
60        private long sampleSize;
61        private SampleFlags sampleFlags;
62        private int sampleCompositionTimeOffset;
63
64        public Entry() {
65        }
66
67        public Entry(long sampleDuration, long sampleSize, SampleFlags sampleFlags, int sampleCompositionTimeOffset) {
68            this.sampleDuration = sampleDuration;
69            this.sampleSize = sampleSize;
70            this.sampleFlags = sampleFlags;
71            this.sampleCompositionTimeOffset = sampleCompositionTimeOffset;
72        }
73
74        public long getSampleDuration() {
75            return sampleDuration;
76        }
77
78        public long getSampleSize() {
79            return sampleSize;
80        }
81
82        public SampleFlags getSampleFlags() {
83            return sampleFlags;
84        }
85
86        public int getSampleCompositionTimeOffset() {
87            return sampleCompositionTimeOffset;
88        }
89
90        public void setSampleDuration(long sampleDuration) {
91            this.sampleDuration = sampleDuration;
92        }
93
94        public void setSampleSize(long sampleSize) {
95            this.sampleSize = sampleSize;
96        }
97
98        public void setSampleFlags(SampleFlags sampleFlags) {
99            this.sampleFlags = sampleFlags;
100        }
101
102        public void setSampleCompositionTimeOffset(int sampleCompositionTimeOffset) {
103            this.sampleCompositionTimeOffset = sampleCompositionTimeOffset;
104        }
105
106        @Override
107        public String toString() {
108            return "Entry{" +
109                    "sampleDuration=" + sampleDuration +
110                    ", sampleSize=" + sampleSize +
111                    ", sampleFlags=" + sampleFlags +
112                    ", sampleCompositionTimeOffset=" + sampleCompositionTimeOffset +
113                    '}';
114        }
115    }
116
117    public void setDataOffset(int dataOffset) {
118        if (dataOffset == -1) {
119            setFlags(getFlags() & (0xFFFFFF ^ 1));
120        } else {
121            setFlags(getFlags() | 0x1); // turn on dataoffset
122        }
123        this.dataOffset = dataOffset;
124    }
125
126    public long[] getSampleCompositionTimeOffsets() {
127        if (isSampleCompositionTimeOffsetPresent()) {
128            long[] result = new long[entries.size()];
129
130            for (int i = 0; i < result.length; i++) {
131                result[i] = entries.get(i).getSampleCompositionTimeOffset();
132            }
133            return result;
134        }
135        return null;
136    }
137
138    public TrackExtendsBox getTrackExtendsBox() {
139        final TrackFragmentHeaderBox tfhd = ((TrackFragmentBox) getParent()).getTrackFragmentHeaderBox();
140        final List<MovieBox> movieBoxes = tfhd.getIsoFile().getBoxes(MovieBox.class);
141        if (movieBoxes.size() == 0) {
142            return null;
143        }
144
145        final List<TrackExtendsBox> trexBoxes = movieBoxes.get(0).getBoxes(TrackExtendsBox.class, true);
146        TrackExtendsBox trex = null;
147        for (TrackExtendsBox aTrex : trexBoxes) {
148            if (aTrex.getTrackId() == tfhd.getTrackId()) {
149                trex = aTrex;
150            }
151        }
152        return trex;
153    }
154
155    public TrackRunBox() {
156        super(TYPE);
157    }
158
159    protected long getContentSize() {
160        long size = 8;
161        int flags = getFlags();
162
163        if ((flags & 0x1) == 0x1) { //dataOffsetPresent
164            size += 4;
165        }
166        if ((flags & 0x4) == 0x4) { //firstSampleFlagsPresent
167            size += 4;
168        }
169
170        long entrySize = 0;
171        if ((flags & 0x100) == 0x100) { //sampleDurationPresent
172            entrySize += 4;
173        }
174        if ((flags & 0x200) == 0x200) { //sampleSizePresent
175            entrySize += 4;
176        }
177        if ((flags & 0x400) == 0x400) { //sampleFlagsPresent
178            entrySize += 4;
179        }
180        if ((flags & 0x800) == 0x800) { //sampleCompositionTimeOffsetPresent
181            entrySize += 4;
182        }
183        size += entrySize * entries.size();
184        return size;
185    }
186
187    protected void getContent(ByteBuffer byteBuffer) {
188        writeVersionAndFlags(byteBuffer);
189        IsoTypeWriter.writeUInt32(byteBuffer, entries.size());
190        int flags = getFlags();
191
192        if ((flags & 0x1) == 1) { //dataOffsetPresent
193            IsoTypeWriter.writeUInt32(byteBuffer, dataOffset);
194        }
195        if ((flags & 0x4) == 0x4) { //firstSampleFlagsPresent
196            firstSampleFlags.getContent(byteBuffer);
197        }
198
199        for (Entry entry : entries) {
200            if ((flags & 0x100) == 0x100) { //sampleDurationPresent
201                IsoTypeWriter.writeUInt32(byteBuffer, entry.sampleDuration);
202            }
203            if ((flags & 0x200) == 0x200) { //sampleSizePresent
204                IsoTypeWriter.writeUInt32(byteBuffer, entry.sampleSize);
205            }
206            if ((flags & 0x400) == 0x400) { //sampleFlagsPresent
207                entry.sampleFlags.getContent(byteBuffer);
208            }
209            if ((flags & 0x800) == 0x800) { //sampleCompositionTimeOffsetPresent
210                byteBuffer.putInt(entry.sampleCompositionTimeOffset);
211            }
212        }
213    }
214
215    @Override
216    public void _parseDetails(ByteBuffer content) {
217        parseVersionAndFlags(content);
218        long sampleCount = IsoTypeReader.readUInt32(content);
219
220        if ((getFlags() & 0x1) == 1) { //dataOffsetPresent
221            dataOffset = l2i(IsoTypeReader.readUInt32(content));
222        } else {
223            dataOffset = -1;
224        }
225        if ((getFlags() & 0x4) == 0x4) { //firstSampleFlagsPresent
226            firstSampleFlags = new SampleFlags(content);
227        }
228
229        for (int i = 0; i < sampleCount; i++) {
230            Entry entry = new Entry();
231            if ((getFlags() & 0x100) == 0x100) { //sampleDurationPresent
232                entry.sampleDuration = IsoTypeReader.readUInt32(content);
233            }
234            if ((getFlags() & 0x200) == 0x200) { //sampleSizePresent
235                entry.sampleSize = IsoTypeReader.readUInt32(content);
236            }
237            if ((getFlags() & 0x400) == 0x400) { //sampleFlagsPresent
238                entry.sampleFlags = new SampleFlags(content);
239            }
240            if ((getFlags() & 0x800) == 0x800) { //sampleCompositionTimeOffsetPresent
241                entry.sampleCompositionTimeOffset = content.getInt();
242            }
243            entries.add(entry);
244        }
245
246    }
247
248    public long getSampleCount() {
249        return entries.size();
250    }
251
252    public boolean isDataOffsetPresent() {
253        return (getFlags() & 0x1) == 1;
254    }
255
256    public boolean isFirstSampleFlagsPresent() {
257        return (getFlags() & 0x4) == 0x4;
258    }
259
260
261    public boolean isSampleSizePresent() {
262        return (getFlags() & 0x200) == 0x200;
263    }
264
265    public boolean isSampleDurationPresent() {
266        return (getFlags() & 0x100) == 0x100;
267    }
268
269    public boolean isSampleFlagsPresent() {
270        return (getFlags() & 0x400) == 0x400;
271    }
272
273    public boolean isSampleCompositionTimeOffsetPresent() {
274        return (getFlags() & 0x800) == 0x800;
275    }
276
277    public void setDataOffsetPresent(boolean v) {
278        if (v) {
279            setFlags(getFlags() | 0x01);
280        } else {
281            setFlags(getFlags() & (0xFFFFFF ^ 0x1));
282        }
283    }
284
285    public void setSampleSizePresent(boolean v) {
286        if (v) {
287            setFlags(getFlags() | 0x200);
288        } else {
289            setFlags(getFlags() & (0xFFFFFF ^ 0x200));
290        }
291    }
292
293    public void setSampleDurationPresent(boolean v) {
294
295        if (v) {
296            setFlags(getFlags() | 0x100);
297        } else {
298            setFlags(getFlags() & (0xFFFFFF ^ 0x100));
299        }
300    }
301
302    public void setSampleFlagsPresent(boolean v) {
303        if (v) {
304            setFlags(getFlags() | 0x400);
305        } else {
306            setFlags(getFlags() & (0xFFFFFF ^ 0x400));
307        }
308    }
309
310    public void setSampleCompositionTimeOffsetPresent(boolean v) {
311        if (v) {
312            setFlags(getFlags() | 0x800);
313        } else {
314            setFlags(getFlags() & (0xFFFFFF ^ 0x800));
315        }
316
317    }
318
319    public int getDataOffset() {
320        return dataOffset;
321    }
322
323    public SampleFlags getFirstSampleFlags() {
324        return firstSampleFlags;
325    }
326
327    public void setFirstSampleFlags(SampleFlags firstSampleFlags) {
328        if (firstSampleFlags == null) {
329            setFlags(getFlags() & (0xFFFFFF ^ 0x4));
330        } else {
331            setFlags(getFlags() | 0x4);
332        }
333        this.firstSampleFlags = firstSampleFlags;
334    }
335
336    @Override
337    public String toString() {
338        final StringBuilder sb = new StringBuilder();
339        sb.append("TrackRunBox");
340        sb.append("{sampleCount=").append(entries.size());
341        sb.append(", dataOffset=").append(dataOffset);
342        sb.append(", dataOffsetPresent=").append(isDataOffsetPresent());
343        sb.append(", sampleSizePresent=").append(isSampleSizePresent());
344        sb.append(", sampleDurationPresent=").append(isSampleDurationPresent());
345        sb.append(", sampleFlagsPresentPresent=").append(isSampleFlagsPresent());
346        sb.append(", sampleCompositionTimeOffsetPresent=").append(isSampleCompositionTimeOffsetPresent());
347        sb.append(", firstSampleFlags=").append(firstSampleFlags);
348        sb.append('}');
349        return sb.toString();
350    }
351
352    public void setEntries(List<Entry> entries) {
353        this.entries = entries;
354    }
355}
356