1dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupackage com.coremedia.iso.boxes.apple;
2dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
3dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.IsoTypeReader;
4dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.IsoTypeWriter;
5dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.Utf8;
6dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.AbstractBox;
7dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.Box;
8dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.coremedia.iso.boxes.ContainerBox;
9dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport com.googlecode.mp4parser.util.ByteBufferByteChannel;
10dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
11dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.io.IOException;
12dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.math.BigInteger;
13dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.nio.ByteBuffer;
14dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.Collections;
15dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.List;
16dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuimport java.util.logging.Logger;
17dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
18dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu/**
19dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu *
20dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu */
21dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhupublic abstract class AbstractAppleMetaDataBox extends AbstractBox implements ContainerBox {
22dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    private static Logger LOG = Logger.getLogger(AbstractAppleMetaDataBox.class.getName());
23dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    AppleDataBox appleDataBox = new AppleDataBox();
24dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
25dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public List<Box> getBoxes() {
26dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return Collections.singletonList((Box) appleDataBox);
27dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
28dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
29dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void setBoxes(List<Box> boxes) {
30dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (boxes.size() == 1 && boxes.get(0) instanceof AppleDataBox) {
31dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox = (AppleDataBox) boxes.get(0);
32dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else {
33dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new IllegalArgumentException("This box only accepts one AppleDataBox child");
34dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
35dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
36dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
37dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public <T extends Box> List<T> getBoxes(Class<T> clazz) {
38dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return getBoxes(clazz, false);
39dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
40dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
41dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) {
42dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        //todo recursive?
43dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (clazz.isAssignableFrom(appleDataBox.getClass())) {
44dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return (List<T>) Collections.singletonList(appleDataBox);
45dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
46dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return null;
47dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
48dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
49dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public AbstractAppleMetaDataBox(String type) {
50dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        super(type);
51dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
52dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
53dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    @Override
54dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void _parseDetails(ByteBuffer content) {
55dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        long dataBoxSize = IsoTypeReader.readUInt32(content);
56dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        String thisShouldBeData = IsoTypeReader.read4cc(content);
57dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        assert "data".equals(thisShouldBeData);
58dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        appleDataBox = new AppleDataBox();
59dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        try {
60dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.parse(new ByteBufferByteChannel(content), null, content.remaining(), null);
61dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } catch (IOException e) {
62dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new RuntimeException(e);
63dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
64dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        appleDataBox.setParent(this);
65dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
66dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
67dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
68dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    protected long getContentSize() {
69dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return appleDataBox.getSize();
70dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
71dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
72dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    protected void getContent(ByteBuffer byteBuffer) {
73dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        try {
74dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.getBox(new ByteBufferByteChannel(byteBuffer));
75dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } catch (IOException e) {
76dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            throw new RuntimeException("The Channel is based on a ByteBuffer and therefore it shouldn't throw any exception");
77dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
78dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
79dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
80dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public long getNumOfBytesToFirstChild() {
81dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return getSize() - appleDataBox.getSize();
82dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
83dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
84dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    @Override
85dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public String toString() {
86dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return this.getClass().getSimpleName() + "{" +
87dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                "appleDataBox=" + getValue() +
88dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                '}';
89dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
90dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
91dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    static long toLong(byte b) {
92dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return b < 0 ? b + 256 : b;
93dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
94dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
95dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public void setValue(String value) {
96dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (appleDataBox.getFlags() == 1) {
97dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox = new AppleDataBox();
98dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setVersion(0);
99dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setFlags(1);
100dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setFourBytes(new byte[4]);
101dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setData(Utf8.convert(value));
102dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else if (appleDataBox.getFlags() == 21) {
103dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            byte[] content = appleDataBox.getData();
104dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox = new AppleDataBox();
105dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setVersion(0);
106dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setFlags(21);
107dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setFourBytes(new byte[4]);
108dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
109dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            ByteBuffer bb = ByteBuffer.allocate(content.length);
110dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            if (content.length == 1) {
111dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                IsoTypeWriter.writeUInt8(bb, (Byte.parseByte(value) & 0xFF));
112dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else if (content.length == 2) {
113dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                IsoTypeWriter.writeUInt16(bb, Integer.parseInt(value));
114dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else if (content.length == 4) {
115dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                IsoTypeWriter.writeUInt32(bb, Long.parseLong(value));
116dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else if (content.length == 8) {
117dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                IsoTypeWriter.writeUInt64(bb, Long.parseLong(value));
118dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            } else {
119dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                throw new Error("The content length within the appleDataBox is neither 1, 2, 4 or 8. I can't handle that!");
120dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
121dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setData(bb.array());
122dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else if (appleDataBox.getFlags() == 0) {
123dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox = new AppleDataBox();
124dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setVersion(0);
125dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setFlags(0);
126dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setFourBytes(new byte[4]);
127dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            appleDataBox.setData(hexStringToByteArray(value));
128dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
129dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else {
130dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            LOG.warning("Don't know how to handle appleDataBox with flag=" + appleDataBox.getFlags());
131dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
132dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
133dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
134dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public String getValue() {
135dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        if (appleDataBox.getFlags() == 1) {
136dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return Utf8.convert(appleDataBox.getData());
137dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else if (appleDataBox.getFlags() == 21) {
138dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            byte[] content = appleDataBox.getData();
139dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            long l = 0;
140dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int current = 1;
141dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            int length = content.length;
142dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            for (byte b : content) {
143dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                l += toLong(b) << (8 * (length - current++));
144dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            }
145dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return "" + l;
146dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else if (appleDataBox.getFlags() == 0) {
147dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return String.format("%x", new BigInteger(appleDataBox.getData()));
148dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        } else {
149dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            return "unknown";
150dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
151dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
152dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
153dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    public static byte[] hexStringToByteArray(String s) {
154dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        int len = s.length();
155dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        byte[] data = new byte[len / 2];
156dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        for (int i = 0; i < len; i += 2) {
157dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
158dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu                    + Character.digit(s.charAt(i + 1), 16));
159dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        }
160dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu        return data;
161dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    }
162dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
163dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
164dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu}
165