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.BoxParser;
21import com.coremedia.iso.ChannelHelper;
22import com.coremedia.iso.IsoTypeWriter;
23import com.googlecode.mp4parser.AbstractBox;
24
25import java.io.IOException;
26import java.nio.ByteBuffer;
27import java.nio.channels.FileChannel;
28import java.nio.channels.ReadableByteChannel;
29import java.nio.channels.WritableByteChannel;
30import java.util.LinkedList;
31import java.util.List;
32
33import static com.googlecode.mp4parser.util.CastUtils.l2i;
34
35/**
36 * A free box. Just a placeholder to enable editing without rewriting the whole file.
37 */
38public class FreeBox implements Box {
39    public static final String TYPE = "free";
40    ByteBuffer data;
41    List<Box> replacers = new LinkedList<Box>();
42    private ContainerBox parent;
43
44    public FreeBox() {
45    }
46
47    public FreeBox(int size) {
48        this.data = ByteBuffer.allocate(size);
49    }
50
51
52    public ByteBuffer getData() {
53        return data;
54    }
55
56    public void setData(ByteBuffer data) {
57        this.data = data;
58    }
59
60    public void getBox(WritableByteChannel os) throws IOException {
61        for (Box replacer : replacers) {
62            replacer.getBox(os);
63        }
64        ByteBuffer header = ByteBuffer.allocate(8);
65        IsoTypeWriter.writeUInt32(header, 8 + data.limit());
66        header.put(TYPE.getBytes());
67        header.rewind();
68        os.write(header);
69        data.rewind();
70        os.write(data);
71
72    }
73
74    public ContainerBox getParent() {
75        return parent;
76    }
77
78    public void setParent(ContainerBox parent) {
79        this.parent = parent;
80    }
81
82    public long getSize() {
83        long size = 8;
84        for (Box replacer : replacers) {
85            size += replacer.getSize();
86        }
87        size += data.limit();
88        return size;
89    }
90
91    public String getType() {
92        return TYPE;
93    }
94
95    public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
96        if (readableByteChannel instanceof FileChannel && contentSize > 1024 * 1024) {
97            // It's quite expensive to map a file into the memory. Just do it when the box is larger than a MB.
98            data = ((FileChannel) readableByteChannel).map(FileChannel.MapMode.READ_ONLY, ((FileChannel) readableByteChannel).position(), contentSize);
99            ((FileChannel) readableByteChannel).position(((FileChannel) readableByteChannel).position() + contentSize);
100        } else {
101            assert contentSize < Integer.MAX_VALUE;
102            data = ChannelHelper.readFully(readableByteChannel, contentSize);
103        }
104    }
105
106
107    public void addAndReplace(Box box) {
108        data.position(l2i(box.getSize()));
109        data = data.slice();
110        replacers.add(box);
111    }
112
113
114}