1 package com.googlecode.mp4parser.boxes; 2 3 import com.coremedia.iso.Hex; 4 import com.coremedia.iso.IsoTypeReader; 5 import com.coremedia.iso.IsoTypeWriter; 6 import com.coremedia.iso.boxes.Box; 7 import com.coremedia.iso.boxes.TrackHeaderBox; 8 import com.coremedia.iso.boxes.fragment.TrackFragmentHeaderBox; 9 import com.googlecode.mp4parser.AbstractFullBox; 10 import com.googlecode.mp4parser.boxes.basemediaformat.TrackEncryptionBox; 11 import com.googlecode.mp4parser.util.Path; 12 13 import java.io.IOException; 14 import java.math.BigInteger; 15 import java.nio.ByteBuffer; 16 import java.nio.channels.WritableByteChannel; 17 import java.util.ArrayList; 18 import java.util.Arrays; 19 import java.util.LinkedList; 20 import java.util.List; 21 22 23 public abstract class AbstractSampleEncryptionBox extends AbstractFullBox { 24 int algorithmId = -1; 25 int ivSize = -1; 26 byte[] kid = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; 27 List<Entry> entries = new LinkedList<Entry>(); 28 AbstractSampleEncryptionBox(String type)29 protected AbstractSampleEncryptionBox(String type) { 30 super(type); 31 } 32 getOffsetToFirstIV()33 public int getOffsetToFirstIV() { 34 int offset = (getSize() > (1l << 32) ? 16 : 8); 35 offset += isOverrideTrackEncryptionBoxParameters() ? 20 : 0; 36 offset += 4; //num entries 37 return offset; 38 } 39 40 @Override _parseDetails(ByteBuffer content)41 public void _parseDetails(ByteBuffer content) { 42 parseVersionAndFlags(content); 43 int useThisIvSize = -1; 44 if ((getFlags() & 0x1) > 0) { 45 algorithmId = IsoTypeReader.readUInt24(content); 46 ivSize = IsoTypeReader.readUInt8(content); 47 useThisIvSize = ivSize; 48 kid = new byte[16]; 49 content.get(kid); 50 } else { 51 List<Box> tkhds = Path.getPaths(this, "/moov[0]/trak/tkhd"); 52 for (Box tkhd : tkhds) { 53 if (((TrackHeaderBox) tkhd).getTrackId() == this.getParent().getBoxes(TrackFragmentHeaderBox.class).get(0).getTrackId()) { 54 AbstractTrackEncryptionBox tenc = (AbstractTrackEncryptionBox) Path.getPath(tkhd, "../mdia[0]/minf[0]/stbl[0]/stsd[0]/enc.[0]/sinf[0]/schi[0]/tenc[0]"); 55 if (tenc == null) { 56 tenc = (AbstractTrackEncryptionBox) Path.getPath(tkhd, "../mdia[0]/minf[0]/stbl[0]/stsd[0]/enc.[0]/sinf[0]/schi[0]/uuid[0]"); 57 } 58 useThisIvSize = tenc.getDefaultIvSize(); 59 } 60 } 61 } 62 long numOfEntries = IsoTypeReader.readUInt32(content); 63 64 while (numOfEntries-- > 0) { 65 Entry e = new Entry(); 66 e.iv = new byte[useThisIvSize < 0 ? 8 : useThisIvSize]; // default to 8 67 content.get(e.iv); 68 if ((getFlags() & 0x2) > 0) { 69 int numOfPairs = IsoTypeReader.readUInt16(content); 70 e.pairs = new LinkedList<Entry.Pair>(); 71 while (numOfPairs-- > 0) { 72 e.pairs.add(e.createPair(IsoTypeReader.readUInt16(content), IsoTypeReader.readUInt32(content))); 73 } 74 } 75 entries.add(e); 76 77 } 78 } 79 80 getSampleCount()81 public int getSampleCount() { 82 return entries.size(); 83 } 84 getEntries()85 public List<Entry> getEntries() { 86 return entries; 87 } 88 setEntries(List<Entry> entries)89 public void setEntries(List<Entry> entries) { 90 this.entries = entries; 91 } 92 getAlgorithmId()93 public int getAlgorithmId() { 94 return algorithmId; 95 } 96 setAlgorithmId(int algorithmId)97 public void setAlgorithmId(int algorithmId) { 98 this.algorithmId = algorithmId; 99 } 100 getIvSize()101 public int getIvSize() { 102 return ivSize; 103 } 104 setIvSize(int ivSize)105 public void setIvSize(int ivSize) { 106 this.ivSize = ivSize; 107 } 108 getKid()109 public byte[] getKid() { 110 return kid; 111 } 112 setKid(byte[] kid)113 public void setKid(byte[] kid) { 114 this.kid = kid; 115 } 116 117 isSubSampleEncryption()118 public boolean isSubSampleEncryption() { 119 return (getFlags() & 0x2) > 0; 120 } 121 isOverrideTrackEncryptionBoxParameters()122 public boolean isOverrideTrackEncryptionBoxParameters() { 123 return (getFlags() & 0x1) > 0; 124 } 125 setSubSampleEncryption(boolean b)126 public void setSubSampleEncryption(boolean b) { 127 if (b) { 128 setFlags(getFlags() | 0x2); 129 } else { 130 setFlags(getFlags() & (0xffffff ^ 0x2)); 131 } 132 } 133 setOverrideTrackEncryptionBoxParameters(boolean b)134 public void setOverrideTrackEncryptionBoxParameters(boolean b) { 135 if (b) { 136 setFlags(getFlags() | 0x1); 137 } else { 138 setFlags(getFlags() & (0xffffff ^ 0x1)); 139 } 140 } 141 142 143 @Override getContent(ByteBuffer byteBuffer)144 protected void getContent(ByteBuffer byteBuffer) { 145 writeVersionAndFlags(byteBuffer); 146 if (isOverrideTrackEncryptionBoxParameters()) { 147 IsoTypeWriter.writeUInt24(byteBuffer, algorithmId); 148 IsoTypeWriter.writeUInt8(byteBuffer, ivSize); 149 byteBuffer.put(kid); 150 } 151 IsoTypeWriter.writeUInt32(byteBuffer, entries.size()); 152 for (Entry entry : entries) { 153 if (isOverrideTrackEncryptionBoxParameters()) { 154 byte[] ivFull = new byte[ivSize]; 155 System.arraycopy(entry.iv, 0, ivFull, ivSize - entry.iv.length, entry.iv.length); 156 byteBuffer.put(ivFull); 157 } else { 158 // just put the iv - i don't know any better 159 byteBuffer.put(entry.iv); 160 } 161 if (isSubSampleEncryption()) { 162 IsoTypeWriter.writeUInt16(byteBuffer, entry.pairs.size()); 163 for (Entry.Pair pair : entry.pairs) { 164 IsoTypeWriter.writeUInt16(byteBuffer, pair.clear); 165 IsoTypeWriter.writeUInt32(byteBuffer, pair.encrypted); 166 } 167 } 168 } 169 } 170 171 @Override getContentSize()172 protected long getContentSize() { 173 long contentSize = 4; 174 if (isOverrideTrackEncryptionBoxParameters()) { 175 contentSize += 4; 176 contentSize += kid.length; 177 } 178 contentSize += 4; 179 for (Entry entry : entries) { 180 contentSize += entry.getSize(); 181 } 182 return contentSize; 183 } 184 185 @Override getBox(WritableByteChannel os)186 public void getBox(WritableByteChannel os) throws IOException { 187 super.getBox(os); 188 } 189 createEntry()190 public Entry createEntry() { 191 return new Entry(); 192 } 193 194 public class Entry { 195 public byte[] iv; 196 public List<Pair> pairs = new LinkedList<Pair>(); 197 getSize()198 public int getSize() { 199 int size = 0; 200 if (isOverrideTrackEncryptionBoxParameters()) { 201 size = ivSize; 202 } else { 203 size = iv.length; 204 } 205 206 207 if (isSubSampleEncryption()) { 208 size += 2; 209 for (Entry.Pair pair : pairs) { 210 size += 6; 211 } 212 } 213 return size; 214 } 215 createPair(int clear, long encrypted)216 public Pair createPair(int clear, long encrypted) { 217 return new Pair(clear, encrypted); 218 } 219 220 221 public class Pair { 222 public int clear; 223 public long encrypted; 224 Pair(int clear, long encrypted)225 public Pair(int clear, long encrypted) { 226 this.clear = clear; 227 this.encrypted = encrypted; 228 } 229 230 @Override equals(Object o)231 public boolean equals(Object o) { 232 if (this == o) { 233 return true; 234 } 235 if (o == null || getClass() != o.getClass()) { 236 return false; 237 } 238 239 Pair pair = (Pair) o; 240 241 if (clear != pair.clear) { 242 return false; 243 } 244 if (encrypted != pair.encrypted) { 245 return false; 246 } 247 248 return true; 249 } 250 251 @Override hashCode()252 public int hashCode() { 253 int result = clear; 254 result = 31 * result + (int) (encrypted ^ (encrypted >>> 32)); 255 return result; 256 } 257 258 @Override toString()259 public String toString() { 260 return "clr:" + clear + " enc:" + encrypted; 261 } 262 } 263 264 265 @Override equals(Object o)266 public boolean equals(Object o) { 267 if (this == o) { 268 return true; 269 } 270 if (o == null || getClass() != o.getClass()) { 271 return false; 272 } 273 274 Entry entry = (Entry) o; 275 276 if (!new BigInteger(iv).equals(new BigInteger(entry.iv))) { 277 return false; 278 } 279 if (pairs != null ? !pairs.equals(entry.pairs) : entry.pairs != null) { 280 return false; 281 } 282 283 return true; 284 } 285 286 @Override hashCode()287 public int hashCode() { 288 int result = iv != null ? Arrays.hashCode(iv) : 0; 289 result = 31 * result + (pairs != null ? pairs.hashCode() : 0); 290 return result; 291 } 292 293 @Override toString()294 public String toString() { 295 return "Entry{" + 296 "iv=" + Hex.encodeHex(iv) + 297 ", pairs=" + pairs + 298 '}'; 299 } 300 } 301 302 @Override equals(Object o)303 public boolean equals(Object o) { 304 if (this == o) { 305 return true; 306 } 307 if (o == null || getClass() != o.getClass()) { 308 return false; 309 } 310 311 AbstractSampleEncryptionBox that = (AbstractSampleEncryptionBox) o; 312 313 if (algorithmId != that.algorithmId) { 314 return false; 315 } 316 if (ivSize != that.ivSize) { 317 return false; 318 } 319 if (entries != null ? !entries.equals(that.entries) : that.entries != null) { 320 return false; 321 } 322 if (!Arrays.equals(kid, that.kid)) { 323 return false; 324 } 325 326 return true; 327 } 328 329 @Override hashCode()330 public int hashCode() { 331 int result = algorithmId; 332 result = 31 * result + ivSize; 333 result = 31 * result + (kid != null ? Arrays.hashCode(kid) : 0); 334 result = 31 * result + (entries != null ? entries.hashCode() : 0); 335 return result; 336 } 337 getEntrySizes()338 public List<Short> getEntrySizes() { 339 List<Short> entrySizes = new ArrayList<Short>(entries.size()); 340 for (Entry entry : entries) { 341 short size = (short) entry.iv.length; 342 if (isSubSampleEncryption()) { 343 size += 2; //numPairs 344 size += entry.pairs.size() * 6; 345 } 346 entrySizes.add(size); 347 } 348 return entrySizes; 349 } 350 } 351