Introduce CryptoData parameter object

This will allow supporting more encryption schemes. Including some
that require more encryption data, like the encryption pattern.

Issue:#1661
Issue:#1989
Issue:#2089

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=155481889
This commit is contained in:
aquilescanta 2017-05-09 02:20:43 -07:00 committed by Oliver Woodman
parent 0302fd6b4d
commit 1f43fb1998
10 changed files with 114 additions and 55 deletions

View File

@ -30,5 +30,6 @@ track 1:
time = 0
flags = 1073741824
data = length 39, hash B7FE77F4
crypto mode = 1
encryption key = length 16, hash 4CE944CF
tracksEnded = true

View File

@ -30,5 +30,6 @@ track 1:
time = 0
flags = 1073741824
data = length 24, hash E58668B1
crypto mode = 1
encryption key = length 16, hash 4CE944CF
tracksEnded = true

View File

@ -83,12 +83,12 @@ public final class C {
public static final String UTF16_NAME = "UTF-16";
/**
* * The name of the serif font family.
* The name of the serif font family.
*/
public static final String SERIF_NAME = "serif";
/**
* * The name of the sans-serif font family.
* The name of the sans-serif font family.
*/
public static final String SANS_SERIF_NAME = "sans-serif";

View File

@ -366,8 +366,9 @@ public final class DefaultTrackOutput implements TrackOutput {
}
// Populate the cryptoInfo.
CryptoData cryptoData = extrasHolder.cryptoData;
buffer.cryptoInfo.set(subsampleCount, clearDataSizes, encryptedDataSizes,
extrasHolder.encryptionKeyId, buffer.cryptoInfo.iv, C.CRYPTO_MODE_AES_CTR);
cryptoData.encryptionKey, buffer.cryptoInfo.iv, cryptoData.cryptoMode);
// Adjust the offset and size to take into account the bytes read.
int bytesRead = (int) (offset - extrasHolder.offset);
@ -516,7 +517,7 @@ public final class DefaultTrackOutput implements TrackOutput {
@Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
byte[] encryptionKey) {
CryptoData cryptoData) {
if (pendingFormatAdjustment) {
format(lastUnadjustedFormat);
}
@ -533,7 +534,7 @@ public final class DefaultTrackOutput implements TrackOutput {
}
timeUs += sampleOffsetUs;
long absoluteOffset = totalBytesWritten - size - offset;
infoQueue.commitSample(timeUs, flags, absoluteOffset, size, encryptionKey);
infoQueue.commitSample(timeUs, flags, absoluteOffset, size, cryptoData);
} finally {
endWriteOperation();
}
@ -606,7 +607,7 @@ public final class DefaultTrackOutput implements TrackOutput {
private int[] sizes;
private int[] flags;
private long[] timesUs;
private byte[][] encryptionKeys;
private CryptoData[] cryptoDatas;
private Format[] formats;
private int queueSize;
@ -628,7 +629,7 @@ public final class DefaultTrackOutput implements TrackOutput {
timesUs = new long[capacity];
flags = new int[capacity];
sizes = new int[capacity];
encryptionKeys = new byte[capacity][];
cryptoDatas = new CryptoData[capacity];
formats = new Format[capacity];
largestDequeuedTimestampUs = Long.MIN_VALUE;
largestQueuedTimestampUs = Long.MIN_VALUE;
@ -792,7 +793,7 @@ public final class DefaultTrackOutput implements TrackOutput {
buffer.setFlags(flags[relativeReadIndex]);
extrasHolder.size = sizes[relativeReadIndex];
extrasHolder.offset = offsets[relativeReadIndex];
extrasHolder.encryptionKeyId = encryptionKeys[relativeReadIndex];
extrasHolder.cryptoData = cryptoDatas[relativeReadIndex];
largestDequeuedTimestampUs = Math.max(largestDequeuedTimestampUs, buffer.timeUs);
queueSize--;
@ -892,7 +893,7 @@ public final class DefaultTrackOutput implements TrackOutput {
}
public synchronized void commitSample(long timeUs, @C.BufferFlags int sampleFlags, long offset,
int size, byte[] encryptionKey) {
int size, CryptoData cryptoData) {
if (upstreamKeyframeRequired) {
if ((sampleFlags & C.BUFFER_FLAG_KEY_FRAME) == 0) {
return;
@ -905,7 +906,7 @@ public final class DefaultTrackOutput implements TrackOutput {
offsets[relativeWriteIndex] = offset;
sizes[relativeWriteIndex] = size;
flags[relativeWriteIndex] = sampleFlags;
encryptionKeys[relativeWriteIndex] = encryptionKey;
cryptoDatas[relativeWriteIndex] = cryptoData;
formats[relativeWriteIndex] = upstreamFormat;
sourceIds[relativeWriteIndex] = upstreamSourceId;
// Increment the write index.
@ -918,14 +919,14 @@ public final class DefaultTrackOutput implements TrackOutput {
long[] newTimesUs = new long[newCapacity];
int[] newFlags = new int[newCapacity];
int[] newSizes = new int[newCapacity];
byte[][] newEncryptionKeys = new byte[newCapacity][];
CryptoData[] newCryptoDatas = new CryptoData[newCapacity];
Format[] newFormats = new Format[newCapacity];
int beforeWrap = capacity - relativeReadIndex;
System.arraycopy(offsets, relativeReadIndex, newOffsets, 0, beforeWrap);
System.arraycopy(timesUs, relativeReadIndex, newTimesUs, 0, beforeWrap);
System.arraycopy(flags, relativeReadIndex, newFlags, 0, beforeWrap);
System.arraycopy(sizes, relativeReadIndex, newSizes, 0, beforeWrap);
System.arraycopy(encryptionKeys, relativeReadIndex, newEncryptionKeys, 0, beforeWrap);
System.arraycopy(cryptoDatas, relativeReadIndex, newCryptoDatas, 0, beforeWrap);
System.arraycopy(formats, relativeReadIndex, newFormats, 0, beforeWrap);
System.arraycopy(sourceIds, relativeReadIndex, newSourceIds, 0, beforeWrap);
int afterWrap = relativeReadIndex;
@ -933,14 +934,14 @@ public final class DefaultTrackOutput implements TrackOutput {
System.arraycopy(timesUs, 0, newTimesUs, beforeWrap, afterWrap);
System.arraycopy(flags, 0, newFlags, beforeWrap, afterWrap);
System.arraycopy(sizes, 0, newSizes, beforeWrap, afterWrap);
System.arraycopy(encryptionKeys, 0, newEncryptionKeys, beforeWrap, afterWrap);
System.arraycopy(cryptoDatas, 0, newCryptoDatas, beforeWrap, afterWrap);
System.arraycopy(formats, 0, newFormats, beforeWrap, afterWrap);
System.arraycopy(sourceIds, 0, newSourceIds, beforeWrap, afterWrap);
offsets = newOffsets;
timesUs = newTimesUs;
flags = newFlags;
sizes = newSizes;
encryptionKeys = newEncryptionKeys;
cryptoDatas = newCryptoDatas;
formats = newFormats;
sourceIds = newSourceIds;
relativeReadIndex = 0;
@ -990,7 +991,7 @@ public final class DefaultTrackOutput implements TrackOutput {
public int size;
public long offset;
public long nextOffset;
public byte[] encryptionKeyId;
public CryptoData cryptoData;
}

View File

@ -51,7 +51,7 @@ public final class DummyTrackOutput implements TrackOutput {
@Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
byte[] encryptionKey) {
CryptoData cryptoData) {
// Do nothing.
}

View File

@ -20,12 +20,54 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
/**
* Receives track level data extracted by an {@link Extractor}.
*/
public interface TrackOutput {
/**
* Holds data required to decrypt a sample.
*/
final class CryptoData {
/**
* The encryption mode used for the sample.
*/
@C.CryptoMode public final int cryptoMode;
/**
* The encryption key associated with the sample. Its contents must not be modified.
*/
public final byte[] encryptionKey;
public CryptoData(@C.CryptoMode int cryptoMode, byte[] encryptionKey) {
this.cryptoMode = cryptoMode;
this.encryptionKey = encryptionKey;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
CryptoData other = (CryptoData) obj;
return cryptoMode == other.cryptoMode && Arrays.equals(encryptionKey, other.encryptionKey);
}
@Override
public int hashCode() {
int result = cryptoMode;
result = 31 * result + Arrays.hashCode(encryptionKey);
return result;
}
}
/**
* Called when the {@link Format} of the track has been extracted from the stream.
*
@ -70,9 +112,9 @@ public interface TrackOutput {
* {@link #sampleData(ExtractorInput, int, boolean)} or
* {@link #sampleData(ParsableByteArray, int)} since the last byte belonging to the sample
* whose metadata is being passed.
* @param encryptionKey The encryption key associated with the sample. May be null.
* @param encryptionData The encryption data required to decrypt the sample. May be null.
*/
void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
byte[] encryptionKey);
CryptoData encryptionData);
}

View File

@ -580,11 +580,11 @@ public final class MatroskaExtractor implements Extractor {
break;
case ID_CONTENT_ENCODING:
if (currentTrack.hasContentEncryption) {
if (currentTrack.encryptionKeyId == null) {
if (currentTrack.cryptoData == null) {
throw new ParserException("Encrypted Track found but ContentEncKeyID was not found");
}
currentTrack.drmInitData = new DrmInitData(
new SchemeData(C.UUID_NIL, MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId));
currentTrack.drmInitData = new DrmInitData(new SchemeData(C.UUID_NIL,
MimeTypes.VIDEO_WEBM, currentTrack.cryptoData.encryptionKey));
}
break;
case ID_CONTENT_ENCODINGS:
@ -888,8 +888,9 @@ public final class MatroskaExtractor implements Extractor {
input.readFully(currentTrack.sampleStrippedBytes, 0, contentSize);
break;
case ID_CONTENT_ENCRYPTION_KEY_ID:
currentTrack.encryptionKeyId = new byte[contentSize];
input.readFully(currentTrack.encryptionKeyId, 0, contentSize);
byte[] encryptionKey = new byte[contentSize];
input.readFully(encryptionKey, 0, contentSize);
currentTrack.cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionKey);
break;
case ID_SIMPLE_BLOCK:
case ID_BLOCK:
@ -1033,7 +1034,7 @@ public final class MatroskaExtractor implements Extractor {
if (CODEC_ID_SUBRIP.equals(track.codecId)) {
writeSubripSample(track);
}
track.output.sampleMetadata(timeUs, blockFlags, sampleBytesWritten, 0, track.encryptionKeyId);
track.output.sampleMetadata(timeUs, blockFlags, sampleBytesWritten, 0, track.cryptoData);
sampleRead = true;
resetSample();
}
@ -1470,7 +1471,7 @@ public final class MatroskaExtractor implements Extractor {
public int defaultSampleDurationNs;
public boolean hasContentEncryption;
public byte[] sampleStrippedBytes;
public byte[] encryptionKeyId;
public TrackOutput.CryptoData cryptoData;
public byte[] codecPrivate;
public DrmInitData drmInitData;

View File

@ -1122,19 +1122,30 @@ public final class FragmentedMp4Extractor implements Extractor {
}
long sampleTimeUs = fragment.getSamplePresentationTime(sampleIndex) * 1000L;
@C.BufferFlags int sampleFlags = (fragment.definesEncryptionData ? C.BUFFER_FLAG_ENCRYPTED : 0)
| (fragment.sampleIsSyncFrameTable[sampleIndex] ? C.BUFFER_FLAG_KEY_FRAME : 0);
int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex;
byte[] encryptionKey = null;
if (fragment.definesEncryptionData) {
encryptionKey = fragment.trackEncryptionBox != null
? fragment.trackEncryptionBox.keyId
: track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex].keyId;
}
if (timestampAdjuster != null) {
sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs);
}
output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, encryptionKey);
@C.BufferFlags int sampleFlags = (fragment.definesEncryptionData ? C.BUFFER_FLAG_ENCRYPTED : 0)
| (fragment.sampleIsSyncFrameTable[sampleIndex] ? C.BUFFER_FLAG_KEY_FRAME : 0);
// Encryption data.
TrackOutput.CryptoData cryptoData = null;
TrackEncryptionBox encryptionBox = null;
if (fragment.definesEncryptionData) {
encryptionBox = fragment.trackEncryptionBox != null
? fragment.trackEncryptionBox
: track.sampleDescriptionEncryptionBoxes[fragment.header.sampleDescriptionIndex];
if (encryptionBox != currentTrackBundle.cachedEncryptionBox) {
cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionBox.keyId);
} else {
cryptoData = currentTrackBundle.cachedCryptoData;
}
}
currentTrackBundle.cachedCryptoData = cryptoData;
currentTrackBundle.cachedEncryptionBox = encryptionBox;
output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, cryptoData);
while (!pendingMetadataSampleInfos.isEmpty()) {
MetadataSampleInfo sampleInfo = pendingMetadataSampleInfos.removeFirst();
@ -1288,6 +1299,10 @@ public final class FragmentedMp4Extractor implements Extractor {
public int currentSampleInTrackRun;
public int currentTrackRunIndex;
// Auxiliary references.
public TrackOutput.CryptoData cachedCryptoData;
public TrackEncryptionBox cachedEncryptionBox;
public TrackBundle(TrackOutput output) {
fragment = new TrackFragment();
this.output = output;
@ -1305,6 +1320,8 @@ public final class FragmentedMp4Extractor implements Extractor {
currentSampleIndex = 0;
currentTrackRunIndex = 0;
currentSampleInTrackRun = 0;
cachedCryptoData = null;
cachedEncryptionBox = null;
}
public void updateDrmInitData(DrmInitData drmInitData) {

View File

@ -186,8 +186,8 @@ public final class ChunkExtractorWrapper implements ExtractorOutput {
@Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
byte[] encryptionKey) {
trackOutput.sampleMetadata(timeUs, flags, size, offset, encryptionKey);
CryptoData cryptoData) {
trackOutput.sampleMetadata(timeUs, flags, size, offset, cryptoData);
}
}

View File

@ -36,7 +36,7 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
private final ArrayList<Integer> sampleFlags;
private final ArrayList<Integer> sampleStartOffsets;
private final ArrayList<Integer> sampleEndOffsets;
private final ArrayList<byte[]> sampleEncryptionKeys;
private final ArrayList<CryptoData> cryptoDatas;
private byte[] sampleData;
public Format format;
@ -47,7 +47,7 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
sampleFlags = new ArrayList<>();
sampleStartOffsets = new ArrayList<>();
sampleEndOffsets = new ArrayList<>();
sampleEncryptionKeys = new ArrayList<>();
cryptoDatas = new ArrayList<>();
}
public void clear() {
@ -56,7 +56,7 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
sampleFlags.clear();
sampleStartOffsets.clear();
sampleEndOffsets.clear();
sampleEncryptionKeys.clear();
cryptoDatas.clear();
}
@Override
@ -89,29 +89,24 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
@Override
public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset,
byte[] encryptionKey) {
CryptoData cryptoData) {
sampleTimesUs.add(timeUs);
sampleFlags.add(flags);
sampleStartOffsets.add(sampleData.length - offset - size);
sampleEndOffsets.add(sampleData.length - offset);
sampleEncryptionKeys.add(encryptionKey);
cryptoDatas.add(cryptoData);
}
public void assertSampleCount(int count) {
Assert.assertEquals(count, sampleTimesUs.size());
}
public void assertSample(int index, byte[] data, long timeUs, int flags, byte[] encryptionKey) {
public void assertSample(int index, byte[] data, long timeUs, int flags, CryptoData cryptoData) {
byte[] actualData = getSampleData(index);
MoreAsserts.assertEquals(data, actualData);
Assert.assertEquals(timeUs, (long) sampleTimesUs.get(index));
Assert.assertEquals(flags, (int) sampleFlags.get(index));
byte[] sampleEncryptionKey = sampleEncryptionKeys.get(index);
if (encryptionKey == null) {
Assert.assertEquals(null, sampleEncryptionKey);
} else {
MoreAsserts.assertEquals(encryptionKey, sampleEncryptionKey);
}
Assert.assertEquals(cryptoData, cryptoDatas.get(index));
}
public byte[] getSampleData(int index) {
@ -128,10 +123,10 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
Assert.assertEquals(expected.sampleFlags.get(i), sampleFlags.get(i));
Assert.assertEquals(expected.sampleStartOffsets.get(i), sampleStartOffsets.get(i));
Assert.assertEquals(expected.sampleEndOffsets.get(i), sampleEndOffsets.get(i));
if (expected.sampleEncryptionKeys.get(i) == null) {
Assert.assertNull(sampleEncryptionKeys.get(i));
if (expected.cryptoDatas.get(i) == null) {
Assert.assertNull(cryptoDatas.get(i));
} else {
MoreAsserts.assertEquals(expected.sampleEncryptionKeys.get(i), sampleEncryptionKeys.get(i));
Assert.assertEquals(expected.cryptoDatas.get(i), cryptoDatas.get(i));
}
}
}
@ -172,9 +167,10 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable {
.add("time", sampleTimesUs.get(i))
.add("flags", sampleFlags.get(i))
.add("data", getSampleData(i));
byte[] key = sampleEncryptionKeys.get(i);
if (key != null) {
dumper.add("encryption key", key);
CryptoData cryptoData = cryptoDatas.get(i);
if (cryptoData != null) {
dumper.add("crypto mode", cryptoData.cryptoMode);
dumper.add("encryption key", cryptoData.encryptionKey);
}
dumper.endBlock();
}