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.IsoTypeReaderVariable;
22import com.coremedia.iso.IsoTypeWriter;
23import com.coremedia.iso.IsoTypeWriterVariable;
24import com.googlecode.mp4parser.AbstractFullBox;
25
26import java.io.IOException;
27import java.nio.ByteBuffer;
28import java.util.LinkedList;
29import java.util.List;
30
31/**
32 * aligned(8) class ItemLocationBox extends FullBox(‘iloc’, version, 0) {
33 * unsigned int(4) offset_size;
34 * unsigned int(4) length_size;
35 * unsigned int(4) base_offset_size;
36 * if (version == 1)
37 * unsigned int(4) index_size;
38 * else
39 * unsigned int(4) reserved;
40 * unsigned int(16) item_count;
41 * for (i=0; i<item_count; i++) {
42 * unsigned int(16) item_ID;
43 * if (version == 1) {
44 * unsigned int(12) reserved = 0;
45 * unsigned int(4) construction_method;
46 * }
47 * unsigned int(16) data_reference_index;
48 * unsigned int(base_offset_size*8) base_offset;
49 * unsigned int(16) extent_count;
50 * for (j=0; j<extent_count; j++) {
51 * if ((version == 1) && (index_size > 0)) {
52 * unsigned int(index_size*8) extent_index;
53 * }
54 * unsigned int(offset_size*8) extent_offset;
55 * unsigned int(length_size*8) extent_length;
56 * }
57 * }
58 * }
59 */
60public class ItemLocationBox extends AbstractFullBox {
61    public int offsetSize = 8;
62    public int lengthSize = 8;
63    public int baseOffsetSize = 8;
64    public int indexSize = 0;
65    public List<Item> items = new LinkedList<Item>();
66
67    public static final String TYPE = "iloc";
68
69    public ItemLocationBox() {
70        super(TYPE);
71    }
72
73    @Override
74    protected long getContentSize() {
75        long size = 8;
76        for (Item item : items) {
77            size += item.getSize();
78        }
79        return size;
80    }
81
82
83    @Override
84    protected void getContent(ByteBuffer byteBuffer) {
85        writeVersionAndFlags(byteBuffer);
86        IsoTypeWriter.writeUInt8(byteBuffer, ((offsetSize << 4) | lengthSize));
87        if (getVersion() == 1) {
88            IsoTypeWriter.writeUInt8(byteBuffer, (baseOffsetSize << 4 | indexSize));
89        } else {
90            IsoTypeWriter.writeUInt8(byteBuffer, (baseOffsetSize << 4));
91        }
92        IsoTypeWriter.writeUInt16(byteBuffer, items.size());
93        for (Item item : items) {
94            item.getContent(byteBuffer);
95        }
96    }
97
98    @Override
99    public void _parseDetails(ByteBuffer content) {
100        parseVersionAndFlags(content);
101        int tmp = IsoTypeReader.readUInt8(content);
102        offsetSize = tmp >>> 4;
103        lengthSize = tmp & 0xf;
104        tmp = IsoTypeReader.readUInt8(content);
105        baseOffsetSize = tmp >>> 4;
106
107        if (getVersion() == 1) {
108            indexSize = tmp & 0xf;
109        }
110        int itemCount = IsoTypeReader.readUInt16(content);
111        for (int i = 0; i < itemCount; i++) {
112            items.add(new Item(content));
113        }
114    }
115
116
117    public int getOffsetSize() {
118        return offsetSize;
119    }
120
121    public void setOffsetSize(int offsetSize) {
122        this.offsetSize = offsetSize;
123    }
124
125    public int getLengthSize() {
126        return lengthSize;
127    }
128
129    public void setLengthSize(int lengthSize) {
130        this.lengthSize = lengthSize;
131    }
132
133    public int getBaseOffsetSize() {
134        return baseOffsetSize;
135    }
136
137    public void setBaseOffsetSize(int baseOffsetSize) {
138        this.baseOffsetSize = baseOffsetSize;
139    }
140
141    public int getIndexSize() {
142        return indexSize;
143    }
144
145    public void setIndexSize(int indexSize) {
146        this.indexSize = indexSize;
147    }
148
149    public List<Item> getItems() {
150        return items;
151    }
152
153    public void setItems(List<Item> items) {
154        this.items = items;
155    }
156
157
158    public Item createItem(int itemId, int constructionMethod, int dataReferenceIndex, long baseOffset, List<Extent> extents) {
159        return new Item(itemId, constructionMethod, dataReferenceIndex, baseOffset, extents);
160    }
161
162    Item createItem(ByteBuffer bb) {
163        return new Item(bb);
164    }
165
166    public class Item {
167        public int itemId;
168        public int constructionMethod;
169        public int dataReferenceIndex;
170        public long baseOffset;
171        public List<Extent> extents = new LinkedList<Extent>();
172
173        public Item(ByteBuffer in) {
174            itemId = IsoTypeReader.readUInt16(in);
175
176            if (getVersion() == 1) {
177                int tmp = IsoTypeReader.readUInt16(in);
178                constructionMethod = tmp & 0xf;
179            }
180
181            dataReferenceIndex = IsoTypeReader.readUInt16(in);
182            if (baseOffsetSize > 0) {
183                baseOffset = IsoTypeReaderVariable.read(in, baseOffsetSize);
184            } else {
185                baseOffset = 0;
186            }
187            int extentCount = IsoTypeReader.readUInt16(in);
188
189
190            for (int i = 0; i < extentCount; i++) {
191                extents.add(new Extent(in));
192            }
193        }
194
195        public Item(int itemId, int constructionMethod, int dataReferenceIndex, long baseOffset, List<Extent> extents) {
196            this.itemId = itemId;
197            this.constructionMethod = constructionMethod;
198            this.dataReferenceIndex = dataReferenceIndex;
199            this.baseOffset = baseOffset;
200            this.extents = extents;
201        }
202
203        public int getSize() {
204            int size = 2;
205
206            if (getVersion() == 1) {
207                size += 2;
208            }
209
210            size += 2;
211            size += baseOffsetSize;
212            size += 2;
213
214
215            for (Extent extent : extents) {
216                size += extent.getSize();
217            }
218            return size;
219        }
220
221        public void setBaseOffset(long baseOffset) {
222            this.baseOffset = baseOffset;
223        }
224
225        public void getContent(ByteBuffer bb)  {
226            IsoTypeWriter.writeUInt16(bb, itemId);
227
228            if (getVersion() == 1) {
229                IsoTypeWriter.writeUInt16(bb, constructionMethod);
230            }
231
232
233            IsoTypeWriter.writeUInt16(bb, dataReferenceIndex);
234            if (baseOffsetSize > 0) {
235                IsoTypeWriterVariable.write(baseOffset, bb, baseOffsetSize);
236            }
237            IsoTypeWriter.writeUInt16(bb, extents.size());
238
239            for (Extent extent : extents) {
240                extent.getContent(bb);
241            }
242        }
243
244        @Override
245        public boolean equals(Object o) {
246            if (this == o) return true;
247            if (o == null || getClass() != o.getClass()) return false;
248
249            Item item = (Item) o;
250
251            if (baseOffset != item.baseOffset) return false;
252            if (constructionMethod != item.constructionMethod) return false;
253            if (dataReferenceIndex != item.dataReferenceIndex) return false;
254            if (itemId != item.itemId) return false;
255            if (extents != null ? !extents.equals(item.extents) : item.extents != null) return false;
256
257            return true;
258        }
259
260        @Override
261        public int hashCode() {
262            int result = itemId;
263            result = 31 * result + constructionMethod;
264            result = 31 * result + dataReferenceIndex;
265            result = 31 * result + (int) (baseOffset ^ (baseOffset >>> 32));
266            result = 31 * result + (extents != null ? extents.hashCode() : 0);
267            return result;
268        }
269
270        @Override
271        public String toString() {
272            return "Item{" +
273                    "baseOffset=" + baseOffset +
274                    ", itemId=" + itemId +
275                    ", constructionMethod=" + constructionMethod +
276                    ", dataReferenceIndex=" + dataReferenceIndex +
277                    ", extents=" + extents +
278                    '}';
279        }
280    }
281
282
283    public Extent createExtent(long extentOffset, long extentLength, long extentIndex) {
284        return new Extent(extentOffset, extentLength, extentIndex);
285    }
286
287    Extent createExtent(ByteBuffer bb) {
288        return new Extent(bb);
289    }
290
291
292    public class Extent {
293        public long extentOffset;
294        public long extentLength;
295        public long extentIndex;
296
297        public Extent(long extentOffset, long extentLength, long extentIndex) {
298            this.extentOffset = extentOffset;
299            this.extentLength = extentLength;
300            this.extentIndex = extentIndex;
301        }
302
303
304        public Extent(ByteBuffer in) {
305            if ((getVersion() == 1) && indexSize > 0) {
306                extentIndex = IsoTypeReaderVariable.read(in, indexSize);
307            }
308            extentOffset = IsoTypeReaderVariable.read(in, offsetSize);
309            extentLength = IsoTypeReaderVariable.read(in, lengthSize);
310        }
311
312        public void getContent(ByteBuffer os)  {
313            if ((getVersion() == 1) && indexSize > 0) {
314                IsoTypeWriterVariable.write(extentIndex, os, indexSize);
315            }
316            IsoTypeWriterVariable.write(extentOffset, os, offsetSize);
317            IsoTypeWriterVariable.write(extentLength, os, lengthSize);
318        }
319
320        public int getSize() {
321            return (indexSize > 0 ? indexSize : 0) + offsetSize + lengthSize;
322        }
323
324
325        @Override
326        public boolean equals(Object o) {
327            if (this == o) return true;
328            if (o == null || getClass() != o.getClass()) return false;
329
330            Extent extent = (Extent) o;
331
332            if (extentIndex != extent.extentIndex) return false;
333            if (extentLength != extent.extentLength) return false;
334            if (extentOffset != extent.extentOffset) return false;
335
336            return true;
337        }
338
339        @Override
340        public int hashCode() {
341            int result = (int) (extentOffset ^ (extentOffset >>> 32));
342            result = 31 * result + (int) (extentLength ^ (extentLength >>> 32));
343            result = 31 * result + (int) (extentIndex ^ (extentIndex >>> 32));
344            return result;
345        }
346
347        @Override
348        public String toString() {
349            final StringBuilder sb = new StringBuilder();
350            sb.append("Extent");
351            sb.append("{extentOffset=").append(extentOffset);
352            sb.append(", extentLength=").append(extentLength);
353            sb.append(", extentIndex=").append(extentIndex);
354            sb.append('}');
355            return sb.toString();
356        }
357    }
358
359
360}
361