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.googlecode.mp4parser.AbstractFullBox;
22
23import java.nio.ByteBuffer;
24
25/**
26 * aligned(8) class TrackFragmentHeaderBox
27 * extends FullBox('tfhd', 0, tf_flags){
28 * unsigned int(32) track_ID;
29 * // all the following are optional fields
30 * unsigned int(64) base_data_offset;
31 * unsigned int(32) sample_description_index;
32 * unsigned int(32) default_sample_duration;
33 * unsigned int(32) default_sample_size;
34 * unsigned int(32) default_sample_flags
35 * }
36 */
37public class TrackFragmentHeaderBox extends AbstractFullBox {
38    public static final String TYPE = "tfhd";
39
40    private long trackId;
41    private long baseDataOffset = -1;
42    private long sampleDescriptionIndex;
43    private long defaultSampleDuration = -1;
44    private long defaultSampleSize = -1;
45    private SampleFlags defaultSampleFlags;
46    private boolean durationIsEmpty;
47
48    public TrackFragmentHeaderBox() {
49        super(TYPE);
50    }
51
52    protected long getContentSize() {
53        long size = 8;
54        int flags = getFlags();
55        if ((flags & 0x1) == 1) { //baseDataOffsetPresent
56            size += 8;
57        }
58        if ((flags & 0x2) == 0x2) { //sampleDescriptionIndexPresent
59            size += 4;
60        }
61        if ((flags & 0x8) == 0x8) { //defaultSampleDurationPresent
62            size += 4;
63        }
64        if ((flags & 0x10) == 0x10) { //defaultSampleSizePresent
65            size += 4;
66        }
67        if ((flags & 0x20) == 0x20) { //defaultSampleFlagsPresent
68            size += 4;
69        }
70        return size;
71    }
72
73
74    protected void getContent(ByteBuffer byteBuffer) {
75        writeVersionAndFlags(byteBuffer);
76        IsoTypeWriter.writeUInt32(byteBuffer, trackId);
77
78        if ((getFlags() & 0x1) == 1) { //baseDataOffsetPresent
79            IsoTypeWriter.writeUInt64(byteBuffer, getBaseDataOffset());
80        }
81        if ((getFlags() & 0x2) == 0x2) { //sampleDescriptionIndexPresent
82            IsoTypeWriter.writeUInt32(byteBuffer, getSampleDescriptionIndex());
83        }
84        if ((getFlags() & 0x8) == 0x8) { //defaultSampleDurationPresent
85            IsoTypeWriter.writeUInt32(byteBuffer, getDefaultSampleDuration());
86        }
87        if ((getFlags() & 0x10) == 0x10) { //defaultSampleSizePresent
88            IsoTypeWriter.writeUInt32(byteBuffer, getDefaultSampleSize());
89        }
90        if ((getFlags() & 0x20) == 0x20) { //defaultSampleFlagsPresent
91            defaultSampleFlags.getContent(byteBuffer);
92        }
93    }
94
95    @Override
96    public void _parseDetails(ByteBuffer content) {
97        parseVersionAndFlags(content);
98        trackId = IsoTypeReader.readUInt32(content);
99        if ((getFlags() & 0x1) == 1) { //baseDataOffsetPresent
100            baseDataOffset = IsoTypeReader.readUInt64(content);
101        }
102        if ((getFlags() & 0x2) == 0x2) { //sampleDescriptionIndexPresent
103            sampleDescriptionIndex = IsoTypeReader.readUInt32(content);
104        }
105        if ((getFlags() & 0x8) == 0x8) { //defaultSampleDurationPresent
106            defaultSampleDuration = IsoTypeReader.readUInt32(content);
107        }
108        if ((getFlags() & 0x10) == 0x10) { //defaultSampleSizePresent
109            defaultSampleSize = IsoTypeReader.readUInt32(content);
110        }
111        if ((getFlags() & 0x20) == 0x20) { //defaultSampleFlagsPresent
112            defaultSampleFlags = new SampleFlags(content);
113        }
114        if ((getFlags() & 0x10000) == 0x10000) { //durationIsEmpty
115            durationIsEmpty = true;
116        }
117    }
118
119    public boolean hasBaseDataOffset() {
120        return (getFlags() & 0x1) != 0;
121    }
122
123    public boolean hasSampleDescriptionIndex() {
124        return (getFlags() & 0x2) != 0;
125    }
126
127    public boolean hasDefaultSampleDuration() {
128        return (getFlags() & 0x8) != 0;
129    }
130
131    public boolean hasDefaultSampleSize() {
132        return (getFlags() & 0x10) != 0;
133    }
134
135    public boolean hasDefaultSampleFlags() {
136        return (getFlags() & 0x20) != 0;
137    }
138
139    public long getTrackId() {
140        return trackId;
141    }
142
143    public long getBaseDataOffset() {
144        return baseDataOffset;
145    }
146
147    public long getSampleDescriptionIndex() {
148        return sampleDescriptionIndex;
149    }
150
151    public long getDefaultSampleDuration() {
152        return defaultSampleDuration;
153    }
154
155    public long getDefaultSampleSize() {
156        return defaultSampleSize;
157    }
158
159    public SampleFlags getDefaultSampleFlags() {
160        return defaultSampleFlags;
161    }
162
163    public boolean isDurationIsEmpty() {
164        return durationIsEmpty;
165    }
166
167    public void setTrackId(long trackId) {
168        this.trackId = trackId;
169    }
170
171    public void setBaseDataOffset(long baseDataOffset) {
172        if (baseDataOffset == -1) {
173            setFlags(getFlags() & (Integer.MAX_VALUE ^ 0x1));
174        } else {
175            setFlags(getFlags() | 0x1); // activate the field
176        }
177        this.baseDataOffset = baseDataOffset;
178    }
179
180    public void setSampleDescriptionIndex(long sampleDescriptionIndex) {
181        if (sampleDescriptionIndex == -1) {
182            setFlags(getFlags() & (Integer.MAX_VALUE ^ 0x2));
183        } else {
184            setFlags(getFlags() | 0x2); // activate the field
185        }
186        this.sampleDescriptionIndex = sampleDescriptionIndex;
187    }
188
189    public void setDefaultSampleDuration(long defaultSampleDuration) {
190        setFlags(getFlags() | 0x8); // activate the field
191        this.defaultSampleDuration = defaultSampleDuration;
192    }
193
194    public void setDefaultSampleSize(long defaultSampleSize) {
195        setFlags(getFlags() | 0x10); // activate the field
196        this.defaultSampleSize = defaultSampleSize;
197    }
198
199    public void setDefaultSampleFlags(SampleFlags defaultSampleFlags) {
200        setFlags(getFlags() | 0x20); // activate the field
201        this.defaultSampleFlags = defaultSampleFlags;
202    }
203
204    public void setDurationIsEmpty(boolean durationIsEmpty) {
205        setFlags(getFlags() | 0x10000); // activate the field
206        this.durationIsEmpty = durationIsEmpty;
207    }
208
209    @Override
210    public String toString() {
211        final StringBuilder sb = new StringBuilder();
212        sb.append("TrackFragmentHeaderBox");
213        sb.append("{trackId=").append(trackId);
214        sb.append(", baseDataOffset=").append(baseDataOffset);
215        sb.append(", sampleDescriptionIndex=").append(sampleDescriptionIndex);
216        sb.append(", defaultSampleDuration=").append(defaultSampleDuration);
217        sb.append(", defaultSampleSize=").append(defaultSampleSize);
218        sb.append(", defaultSampleFlags=").append(defaultSampleFlags);
219        sb.append(", durationIsEmpty=").append(durationIsEmpty);
220        sb.append('}');
221        return sb.toString();
222    }
223
224}
225