package com.googlecode.mp4parser.boxes.piff;
import com.coremedia.iso.*;
import com.coremedia.iso.boxes.AbstractFullBox;
import com.coremedia.iso.boxes.Box;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* aligned(8) class SampleEncryptionBox extends FullBox(‘uuid’, extended_type= 0xA2394F52-5A9B-4f14-A244-6C427C648DF4, version=0, flags=0)
* {
* <p/>
* unsigned int (32) sample_count;
* {
* unsigned int(16) InitializationVector;
* }[ sample_count ]
* }
*/
public class PiffSampleEncryptionBox extends AbstractFullBox {
int algorithmId = -1;
int ivSize = -1;
byte[] kid = null;
List<Entry> entries = new LinkedList<Entry>();
/**
* Creates a SampleEncryptionBox for non-h264 tracks.
*/
public PiffSampleEncryptionBox() {
super(IsoFile.fourCCtoBytes("uuid"));
}
public List<Entry> getEntries() {
return entries;
}
public void setEntries(List<Entry> entries) {
this.entries = entries;
}
@Override
public byte[] getUserType() {
return new byte[]{(byte) 0xA2, 0x39, 0x4F, 0x52, 0x5A, (byte) 0x9B, 0x4f, 0x14, (byte) 0xA2, 0x44, 0x6C, 0x42, 0x7C, 0x64, (byte) 0x8D, (byte) 0xF4};
}
@Override
protected long getContentSize() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
getContent(new IsoOutputStream(baos));
} catch (IOException e) {
return -1;
}
return baos.toByteArray().length;
}
public boolean isSubSampleEncryption() {
return (entries.get(0).pairs.size() > 0);
}
public boolean isOverrideTrackEncryptionBoxParameters() {
return kid != null && algorithmId > 0 && ivSize > 0;
}
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;
}
@Override
public void getBox(IsoOutputStream os) throws IOException {
setFlags(0x0);
if (isOverrideTrackEncryptionBoxParameters()) {
setFlags(getFlags() | 0x1);
}
if (isSubSampleEncryption()) {
setFlags(getFlags() | 0x2);
}
super.getBox(os); //To change body of overridden methods use File | Settings | File Templates.
}
@Override
public void parse(IsoBufferWrapper in, long size, BoxParser boxParser, Box lastMovieFragmentBox) throws IOException {
super.parse(in, size, boxParser, lastMovieFragmentBox);
if ((getFlags() & 0x1) > 0) {
algorithmId = in.readUInt24();
ivSize = in.readUInt8();
kid = in.read(16);
}
long numOfEntries = in.readUInt32();
while (numOfEntries-- > 0) {
Entry e = new Entry();
e.iv = in.read(((getFlags() & 0x1) > 0) ? ivSize : 8);
if ((getFlags() & 0x2) > 0) {
int numOfPairs = in.readUInt16();
while (numOfPairs-- > 0) {
e.pairs.add(new Entry.Pair(in.readUInt16(), in.readUInt32()));
}
}
entries.add(e);
}
}
@Override
protected void getContent(IsoOutputStream os) throws IOException {
if (isOverrideTrackEncryptionBoxParameters()) {
os.writeUInt24(algorithmId);
os.writeUInt8(ivSize);
os.write(kid);
}
os.writeUInt32(entries.size());
for (Entry entry : entries) {
os.write(entry.iv);
if (isSubSampleEncryption()) {
os.writeUInt16(entry.pairs.size());
for (Entry.Pair pair : entry.pairs) {
os.writeUInt16(pair.clear);
os.writeUInt32(pair.encrypted);
}
}
}
}
public static class Entry {
public byte[] iv;
public List<Pair> pairs = new LinkedList<Pair>();
public static 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 (!Arrays.equals(iv, 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;
PiffSampleEncryptionBox that = (PiffSampleEncryptionBox) o;
if (algorithmId != that.algorithmId) return false;
if (ivSize != that.ivSize) return false;
if (!entries.equals(that.entries)) 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.hashCode();
return result;
}
}