1dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu/*
2dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * Copyright 2012 Sebastian Annies, Hamburg
3dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *
4dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * Licensed under the Apache License, Version 2.0 (the License);
5dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * you may not use this file except in compliance with the License.
6dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * You may obtain a copy of the License at
7dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *
8dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *     http://www.apache.org/licenses/LICENSE-2.0
9dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *
10dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * Unless required by applicable law or agreed to in writing, software
11dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * distributed under the License is distributed on an AS IS BASIS,
12dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * See the License for the specific language governing permissions and
14dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * limitations under the License.
15dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu */
16dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupackage com.coremedia.iso;
17dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
18dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.Box;
19dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.ContainerBox;
20dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.UserBox;
21dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
22dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.IOException;
23dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.nio.ByteBuffer;
24dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.nio.channels.FileChannel;
25dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.nio.channels.ReadableByteChannel;
26dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.logging.Logger;
27dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
28dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport static com.googlecode.mp4parser.util.CastUtils.l2i;
29dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
30dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu/**
31dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu * This BoxParser handles the basic stuff like reading size and extracting box type.
32dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu */
33dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupublic abstract class AbstractBoxParser implements BoxParser {
34dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
35dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private static Logger LOG = Logger.getLogger(AbstractBoxParser.class.getName());
36dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
37dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public abstract Box createBox(String type, byte[] userType, String parent);
38dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
39dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    /**
40dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * Parses the next size and type, creates a box instance and parses the box's content.
41dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     *
42dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @param byteChannel the FileChannel pointing to the ISO file
43dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @param parent      the current box's parent (null if no parent)
44dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @return the box just parsed
45dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     * @throws java.io.IOException if reading from <code>in</code> fails
46dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu     */
47dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public Box parseBox(ReadableByteChannel byteChannel, ContainerBox parent) throws IOException {
48dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
49dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
50dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        ByteBuffer header = ChannelHelper.readFully(byteChannel, 8);
51dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
52dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        long size = IsoTypeReader.readUInt32(header);
53dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        // do plausibility check
54dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (size < 8 && size > 1) {
55dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            LOG.severe("Plausibility check failed: size < 8 (size = " + size + "). Stop parsing!");
56dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return null;
57dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
58dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
59dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
60dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        String type = IsoTypeReader.read4cc(header);
61dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        byte[] usertype = null;
62dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        long contentSize;
63dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
64dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (size == 1) {
65dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            ByteBuffer bb = ByteBuffer.allocate(8);
66dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            byteChannel.read(bb);
67dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            bb.rewind();
68dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            size = IsoTypeReader.readUInt64(bb);
69dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            contentSize = size - 16;
70dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else if (size == 0) {
71dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (byteChannel instanceof FileChannel) {
72dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                size = ((FileChannel) byteChannel).size() - ((FileChannel) byteChannel).position() - 8;
73dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else {
74dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                throw new RuntimeException("Only FileChannel inputs may use size == 0 (box reaches to the end of file)");
75dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
76dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            contentSize = size - 8;
77dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else {
78dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            contentSize = size - 8;
79dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
80dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (UserBox.TYPE.equals(type)) {
81dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            ByteBuffer bb = ByteBuffer.allocate(16);
82dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            byteChannel.read(bb);
83dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            bb.rewind();
84dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            usertype = bb.array();
85dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            contentSize -= 16;
86dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
87dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        Box box = createBox(type, usertype, parent.getType());
88dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        box.setParent(parent);
89dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        LOG.finest("Parsing " + box.getType());
90dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        // System.out.println("parsing " + Arrays.toString(box.getType()) + " " + box.getClass().getName() + " size=" + size);
91dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
92dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
93dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (l2i(size - contentSize) == 8) {
94dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            // default - no large box - no uuid
95dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            // do nothing header's already correct
96dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header.rewind();
97dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else if (l2i(size - contentSize) == 16) {
98dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header = ByteBuffer.allocate(16);
99dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            IsoTypeWriter.writeUInt32(header, 1);
100dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header.put(IsoFile.fourCCtoBytes(type));
101dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            IsoTypeWriter.writeUInt64(header, size);
102dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else if (l2i(size - contentSize) == 24) {
103dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header = ByteBuffer.allocate(24);
104dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            IsoTypeWriter.writeUInt32(header, size);
105dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header.put(IsoFile.fourCCtoBytes(type));
106dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header.put(usertype);
107dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else if (l2i(size - contentSize) == 32) {
108dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header = ByteBuffer.allocate(32);
109dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            IsoTypeWriter.writeUInt32(header, size);
110dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header.put(IsoFile.fourCCtoBytes(type));
111dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            IsoTypeWriter.writeUInt64(header, size);
112dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            header.put(usertype);
113dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else {
114dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new RuntimeException("I didn't expect that");
115dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
116dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
117dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
118dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        box.parse(byteChannel, header, contentSize, this);
119dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        // System.out.println("box = " + box);
120dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
121dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
122dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        assert size == box.getSize() :
123dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                "Reconstructed Size is not x to the number of parsed bytes! (" +
124dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        box.getType() + ")"
125dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                        + " Actual Box size: " + size + " Calculated size: " + box.getSize();
126dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return box;
127dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
128dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
129dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
130dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu}
131