summaryrefslogtreecommitdiff
path: root/isoparser/src/main/java/com/coremedia/iso/AbstractBoxParser.java
blob: 6d92acd1a12f63e797206f1659f7ad677c421e68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * Copyright 2012 Sebastian Annies, Hamburg
 *
 * Licensed under the Apache License, Version 2.0 (the License);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.coremedia.iso;

import com.coremedia.iso.boxes.Box;
import com.coremedia.iso.boxes.ContainerBox;
import com.coremedia.iso.boxes.UserBox;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.logging.Logger;

import static com.googlecode.mp4parser.util.CastUtils.l2i;

/**
 * This BoxParser handles the basic stuff like reading size and extracting box type.
 */
public abstract class AbstractBoxParser implements BoxParser {

    private static Logger LOG = Logger.getLogger(AbstractBoxParser.class.getName());

    public abstract Box createBox(String type, byte[] userType, String parent);

    /**
     * Parses the next size and type, creates a box instance and parses the box's content.
     *
     * @param byteChannel the FileChannel pointing to the ISO file
     * @param parent      the current box's parent (null if no parent)
     * @return the box just parsed
     * @throws java.io.IOException if reading from <code>in</code> fails
     */
    public Box parseBox(ReadableByteChannel byteChannel, ContainerBox parent) throws IOException {


        ByteBuffer header = ChannelHelper.readFully(byteChannel, 8);

        long size = IsoTypeReader.readUInt32(header);
        // do plausibility check
        if (size < 8 && size > 1) {
            LOG.severe("Plausibility check failed: size < 8 (size = " + size + "). Stop parsing!");
            return null;
        }


        String type = IsoTypeReader.read4cc(header);
        byte[] usertype = null;
        long contentSize;

        if (size == 1) {
            ByteBuffer bb = ByteBuffer.allocate(8);
            byteChannel.read(bb);
            bb.rewind();
            size = IsoTypeReader.readUInt64(bb);
            contentSize = size - 16;
        } else if (size == 0) {
            if (byteChannel instanceof FileChannel) {
                size = ((FileChannel) byteChannel).size() - ((FileChannel) byteChannel).position() - 8;
            } else {
                throw new RuntimeException("Only FileChannel inputs may use size == 0 (box reaches to the end of file)");
            }
            contentSize = size - 8;
        } else {
            contentSize = size - 8;
        }
        if (UserBox.TYPE.equals(type)) {
            ByteBuffer bb = ByteBuffer.allocate(16);
            byteChannel.read(bb);
            bb.rewind();
            usertype = bb.array();
            contentSize -= 16;
        }
        Box box = createBox(type, usertype, parent.getType());
        box.setParent(parent);
        LOG.finest("Parsing " + box.getType());
        // System.out.println("parsing " + Arrays.toString(box.getType()) + " " + box.getClass().getName() + " size=" + size);


        if (l2i(size - contentSize) == 8) {
            // default - no large box - no uuid
            // do nothing header's already correct
            header.rewind();
        } else if (l2i(size - contentSize) == 16) {
            header = ByteBuffer.allocate(16);
            IsoTypeWriter.writeUInt32(header, 1);
            header.put(IsoFile.fourCCtoBytes(type));
            IsoTypeWriter.writeUInt64(header, size);
        } else if (l2i(size - contentSize) == 24) {
            header = ByteBuffer.allocate(24);
            IsoTypeWriter.writeUInt32(header, size);
            header.put(IsoFile.fourCCtoBytes(type));
            header.put(usertype);
        } else if (l2i(size - contentSize) == 32) {
            header = ByteBuffer.allocate(32);
            IsoTypeWriter.writeUInt32(header, size);
            header.put(IsoFile.fourCCtoBytes(type));
            IsoTypeWriter.writeUInt64(header, size);
            header.put(usertype);
        } else {
            throw new RuntimeException("I didn't expect that");
        }


        box.parse(byteChannel, header, contentSize, this);
        // System.out.println("box = " + box);


        assert size == box.getSize() :
                "Reconstructed Size is not x to the number of parsed bytes! (" +
                        box.getType() + ")"
                        + " Actual Box size: " + size + " Calculated size: " + box.getSize();
        return box;
    }


}