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.sampleentry;
18
19import com.coremedia.iso.BoxParser;
20import com.coremedia.iso.IsoTypeReader;
21import com.coremedia.iso.IsoTypeWriter;
22import com.googlecode.mp4parser.AbstractBox;
23import com.coremedia.iso.boxes.Box;
24import com.coremedia.iso.boxes.ContainerBox;
25import com.googlecode.mp4parser.util.ByteBufferByteChannel;
26
27import java.io.ByteArrayOutputStream;
28import java.io.IOException;
29import java.nio.ByteBuffer;
30import java.nio.channels.Channels;
31import java.nio.channels.ReadableByteChannel;
32import java.nio.channels.WritableByteChannel;
33import java.util.ArrayList;
34import java.util.LinkedList;
35import java.util.List;
36
37/**
38 * Abstract base class for all sample entries.
39 *
40 * @see com.coremedia.iso.boxes.sampleentry.AudioSampleEntry
41 * @see com.coremedia.iso.boxes.sampleentry.VisualSampleEntry
42 * @see com.coremedia.iso.boxes.sampleentry.TextSampleEntry
43 */
44public abstract class SampleEntry extends AbstractBox implements ContainerBox {
45
46
47    private int dataReferenceIndex = 1;
48    protected List<Box> boxes = new LinkedList<Box>();
49    private BoxParser boxParser;
50
51
52    protected SampleEntry(String type) {
53        super(type);
54    }
55
56    public void setType(String type) {
57        this.type = type;
58    }
59
60    public int getDataReferenceIndex() {
61        return dataReferenceIndex;
62    }
63
64    public void setDataReferenceIndex(int dataReferenceIndex) {
65        this.dataReferenceIndex = dataReferenceIndex;
66    }
67
68    public void setBoxes(List<Box> boxes) {
69        this.boxes = new LinkedList<Box>(boxes);
70    }
71
72    public void addBox(Box b) {
73        b.setParent(this);
74        boxes.add(b);
75    }
76
77    public boolean removeBox(Box b) {
78        b.setParent(this);
79        return boxes.remove(b);
80    }
81
82    public List<Box> getBoxes() {
83        return boxes;
84    }
85
86    @SuppressWarnings("unchecked")
87    public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) {
88        List<T> boxesToBeReturned = new ArrayList<T>(2);
89        for (Box boxe : boxes) { //clazz.isInstance(boxe) / clazz == boxe.getClass()?
90            if (clazz == boxe.getClass()) {
91                boxesToBeReturned.add((T) boxe);
92            }
93
94            if (recursive && boxe instanceof ContainerBox) {
95                boxesToBeReturned.addAll(((ContainerBox) boxe).getBoxes(clazz, recursive));
96            }
97        }
98        // Optimize here! Spare object creation work on arrays directly! System.arrayCopy
99        return boxesToBeReturned;
100        //return (T[]) boxesToBeReturned.toArray();
101    }
102
103    @SuppressWarnings("unchecked")
104    public <T extends Box> List<T> getBoxes(Class<T> clazz) {
105        return getBoxes(clazz, false);
106    }
107
108    @Override
109    public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
110        this.boxParser = boxParser;
111        super.parse(readableByteChannel, header, contentSize, boxParser);
112
113    }
114
115
116    public void _parseReservedAndDataReferenceIndex(ByteBuffer content) {
117        content.get(new byte[6]); // ignore 6 reserved bytes;
118        dataReferenceIndex = IsoTypeReader.readUInt16(content);
119    }
120
121    public void _parseChildBoxes(ByteBuffer content) {
122        while (content.remaining() > 8) {
123            try {
124                boxes.add(boxParser.parseBox(new ByteBufferByteChannel(content), this));
125            } catch (IOException e) {
126                throw new RuntimeException(e);
127            }
128
129        }
130        setDeadBytes(content.slice());
131    }
132
133    public void _writeReservedAndDataReferenceIndex(ByteBuffer bb) {
134        bb.put(new byte[6]);
135        IsoTypeWriter.writeUInt16(bb, dataReferenceIndex);
136    }
137
138    public void _writeChildBoxes(ByteBuffer bb) {
139        ByteArrayOutputStream baos = new ByteArrayOutputStream();
140        WritableByteChannel wbc = Channels.newChannel(baos);
141        try {
142            for (Box box : boxes) {
143                box.getBox(wbc);
144            }
145            wbc.close();
146        } catch (IOException e) {
147            throw new RuntimeException("Cannot happen. Everything should be in memory and therefore no exceptions.");
148        }
149        bb.put(baos.toByteArray());
150    }
151
152    public long getNumOfBytesToFirstChild() {
153        long sizeOfChildren = 0;
154        for (Box box : boxes) {
155            sizeOfChildren += box.getSize();
156        }
157        return getSize() - sizeOfChildren;
158    }
159
160}
161