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