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