mirror of
https://github.com/androidx/media.git
synced 2025-05-12 01:59:50 +08:00
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:
parent
0302fd6b4d
commit
1f43fb1998
@ -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
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user