| package com.googlecode.mp4parser.boxes; |
| |
| import com.coremedia.iso.Hex; |
| import com.coremedia.iso.IsoTypeReader; |
| import com.coremedia.iso.IsoTypeWriter; |
| import com.coremedia.iso.boxes.Box; |
| import com.coremedia.iso.boxes.TrackHeaderBox; |
| import com.coremedia.iso.boxes.fragment.TrackFragmentHeaderBox; |
| import com.googlecode.mp4parser.AbstractFullBox; |
| import com.googlecode.mp4parser.boxes.basemediaformat.TrackEncryptionBox; |
| import com.googlecode.mp4parser.util.Path; |
| |
| import java.io.IOException; |
| import java.math.BigInteger; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.WritableByteChannel; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| |
| public abstract class AbstractSampleEncryptionBox extends AbstractFullBox { |
| int algorithmId = -1; |
| int ivSize = -1; |
| byte[] kid = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; |
| List<Entry> entries = new LinkedList<Entry>(); |
| |
| protected AbstractSampleEncryptionBox(String type) { |
| super(type); |
| } |
| |
| public int getOffsetToFirstIV() { |
| int offset = (getSize() > (1l << 32) ? 16 : 8); |
| offset += isOverrideTrackEncryptionBoxParameters() ? 20 : 0; |
| offset += 4; //num entries |
| return offset; |
| } |
| |
| @Override |
| public void _parseDetails(ByteBuffer content) { |
| parseVersionAndFlags(content); |
| int useThisIvSize = -1; |
| if ((getFlags() & 0x1) > 0) { |
| algorithmId = IsoTypeReader.readUInt24(content); |
| ivSize = IsoTypeReader.readUInt8(content); |
| useThisIvSize = ivSize; |
| kid = new byte[16]; |
| content.get(kid); |
| } else { |
| List<Box> tkhds = Path.getPaths(this, "/moov[0]/trak/tkhd"); |
| for (Box tkhd : tkhds) { |
| if (((TrackHeaderBox) tkhd).getTrackId() == this.getParent().getBoxes(TrackFragmentHeaderBox.class).get(0).getTrackId()) { |
| AbstractTrackEncryptionBox tenc = (AbstractTrackEncryptionBox) Path.getPath(tkhd, "../mdia[0]/minf[0]/stbl[0]/stsd[0]/enc.[0]/sinf[0]/schi[0]/tenc[0]"); |
| if (tenc == null) { |
| tenc = (AbstractTrackEncryptionBox) Path.getPath(tkhd, "../mdia[0]/minf[0]/stbl[0]/stsd[0]/enc.[0]/sinf[0]/schi[0]/uuid[0]"); |
| } |
| useThisIvSize = tenc.getDefaultIvSize(); |
| } |
| } |
| } |
| long numOfEntries = IsoTypeReader.readUInt32(content); |
| |
| while (numOfEntries-- > 0) { |
| Entry e = new Entry(); |
| e.iv = new byte[useThisIvSize < 0 ? 8 : useThisIvSize]; // default to 8 |
| content.get(e.iv); |
| if ((getFlags() & 0x2) > 0) { |
| int numOfPairs = IsoTypeReader.readUInt16(content); |
| e.pairs = new LinkedList<Entry.Pair>(); |
| while (numOfPairs-- > 0) { |
| e.pairs.add(e.createPair(IsoTypeReader.readUInt16(content), IsoTypeReader.readUInt32(content))); |
| } |
| } |
| entries.add(e); |
| |
| } |
| } |
| |
| |
| public int getSampleCount() { |
| return entries.size(); |
| } |
| |
| public List<Entry> getEntries() { |
| return entries; |
| } |
| |
| public void setEntries(List<Entry> entries) { |
| this.entries = entries; |
| } |
| |
| public int getAlgorithmId() { |
| return algorithmId; |
| } |
| |
| public void setAlgorithmId(int algorithmId) { |
| this.algorithmId = algorithmId; |
| } |
| |
| public int getIvSize() { |
| return ivSize; |
| } |
| |
| public void setIvSize(int ivSize) { |
| this.ivSize = ivSize; |
| } |
| |
| public byte[] getKid() { |
| return kid; |
| } |
| |
| public void setKid(byte[] kid) { |
| this.kid = kid; |
| } |
| |
| |
| public boolean isSubSampleEncryption() { |
| return (getFlags() & 0x2) > 0; |
| } |
| |
| public boolean isOverrideTrackEncryptionBoxParameters() { |
| return (getFlags() & 0x1) > 0; |
| } |
| |
| public void setSubSampleEncryption(boolean b) { |
| if (b) { |
| setFlags(getFlags() | 0x2); |
| } else { |
| setFlags(getFlags() & (0xffffff ^ 0x2)); |
| } |
| } |
| |
| public void setOverrideTrackEncryptionBoxParameters(boolean b) { |
| if (b) { |
| setFlags(getFlags() | 0x1); |
| } else { |
| setFlags(getFlags() & (0xffffff ^ 0x1)); |
| } |
| } |
| |
| |
| @Override |
| protected void getContent(ByteBuffer byteBuffer) { |
| writeVersionAndFlags(byteBuffer); |
| if (isOverrideTrackEncryptionBoxParameters()) { |
| IsoTypeWriter.writeUInt24(byteBuffer, algorithmId); |
| IsoTypeWriter.writeUInt8(byteBuffer, ivSize); |
| byteBuffer.put(kid); |
| } |
| IsoTypeWriter.writeUInt32(byteBuffer, entries.size()); |
| for (Entry entry : entries) { |
| if (isOverrideTrackEncryptionBoxParameters()) { |
| byte[] ivFull = new byte[ivSize]; |
| System.arraycopy(entry.iv, 0, ivFull, ivSize - entry.iv.length, entry.iv.length); |
| byteBuffer.put(ivFull); |
| } else { |
| // just put the iv - i don't know any better |
| byteBuffer.put(entry.iv); |
| } |
| if (isSubSampleEncryption()) { |
| IsoTypeWriter.writeUInt16(byteBuffer, entry.pairs.size()); |
| for (Entry.Pair pair : entry.pairs) { |
| IsoTypeWriter.writeUInt16(byteBuffer, pair.clear); |
| IsoTypeWriter.writeUInt32(byteBuffer, pair.encrypted); |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected long getContentSize() { |
| long contentSize = 4; |
| if (isOverrideTrackEncryptionBoxParameters()) { |
| contentSize += 4; |
| contentSize += kid.length; |
| } |
| contentSize += 4; |
| for (Entry entry : entries) { |
| contentSize += entry.getSize(); |
| } |
| return contentSize; |
| } |
| |
| @Override |
| public void getBox(WritableByteChannel os) throws IOException { |
| super.getBox(os); |
| } |
| |
| public Entry createEntry() { |
| return new Entry(); |
| } |
| |
| public class Entry { |
| public byte[] iv; |
| public List<Pair> pairs = new LinkedList<Pair>(); |
| |
| public int getSize() { |
| int size = 0; |
| if (isOverrideTrackEncryptionBoxParameters()) { |
| size = ivSize; |
| } else { |
| size = iv.length; |
| } |
| |
| |
| if (isSubSampleEncryption()) { |
| size += 2; |
| for (Entry.Pair pair : pairs) { |
| size += 6; |
| } |
| } |
| return size; |
| } |
| |
| public Pair createPair(int clear, long encrypted) { |
| return new Pair(clear, encrypted); |
| } |
| |
| |
| public class Pair { |
| public int clear; |
| public long encrypted; |
| |
| public Pair(int clear, long encrypted) { |
| this.clear = clear; |
| this.encrypted = encrypted; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| |
| Pair pair = (Pair) o; |
| |
| if (clear != pair.clear) { |
| return false; |
| } |
| if (encrypted != pair.encrypted) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = clear; |
| result = 31 * result + (int) (encrypted ^ (encrypted >>> 32)); |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| return "clr:" + clear + " enc:" + encrypted; |
| } |
| } |
| |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| |
| Entry entry = (Entry) o; |
| |
| if (!new BigInteger(iv).equals(new BigInteger(entry.iv))) { |
| return false; |
| } |
| if (pairs != null ? !pairs.equals(entry.pairs) : entry.pairs != null) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = iv != null ? Arrays.hashCode(iv) : 0; |
| result = 31 * result + (pairs != null ? pairs.hashCode() : 0); |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| return "Entry{" + |
| "iv=" + Hex.encodeHex(iv) + |
| ", pairs=" + pairs + |
| '}'; |
| } |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| |
| AbstractSampleEncryptionBox that = (AbstractSampleEncryptionBox) o; |
| |
| if (algorithmId != that.algorithmId) { |
| return false; |
| } |
| if (ivSize != that.ivSize) { |
| return false; |
| } |
| if (entries != null ? !entries.equals(that.entries) : that.entries != null) { |
| return false; |
| } |
| if (!Arrays.equals(kid, that.kid)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = algorithmId; |
| result = 31 * result + ivSize; |
| result = 31 * result + (kid != null ? Arrays.hashCode(kid) : 0); |
| result = 31 * result + (entries != null ? entries.hashCode() : 0); |
| return result; |
| } |
| |
| public List<Short> getEntrySizes() { |
| List<Short> entrySizes = new ArrayList<Short>(entries.size()); |
| for (Entry entry : entries) { |
| short size = (short) entry.iv.length; |
| if (isSubSampleEncryption()) { |
| size += 2; //numPairs |
| size += entry.pairs.size() * 6; |
| } |
| entrySizes.add(size); |
| } |
| return entrySizes; |
| } |
| } |