summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/media/framework/java/android/media/MediaParser.java124
1 files changed, 90 insertions, 34 deletions
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index d0692e44a564..070b13b9e592 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -402,9 +402,9 @@ public final class MediaParser {
* onSampleDataFound(int, MediaParser.InputReader)} for the specified track, since the
* last byte belonging to the sample whose metadata is being passed.
* @param cryptoInfo Encryption data required to decrypt the sample. May be null for
- * unencrypted samples. MediaParser may reuse {@link CryptoInfo} instances to avoid
- * allocations, so implementations of this method must not write to or keep reference to
- * the fields of this parameter.
+ * unencrypted samples. Implementors should treat any output {@link CryptoInfo}
+ * instances as immutable. MediaParser will not modify any output {@code cryptoInfos}
+ * and implementors should not modify them either.
*/
void onSampleCompleted(
int trackIndex,
@@ -1409,23 +1409,28 @@ public final class MediaParser {
private class TrackOutputAdapter implements TrackOutput {
private final int mTrackIndex;
- private final CryptoInfo mCryptoInfo;
+
+ private CryptoInfo mLastOutputCryptoInfo;
+ private CryptoInfo.Pattern mLastOutputEncryptionPattern;
+ private CryptoData mLastReceivedCryptoData;
@EncryptionDataReadState private int mEncryptionDataReadState;
private int mEncryptionDataSizeToSubtractFromSampleDataSize;
private int mEncryptionVectorSize;
+ private byte[] mScratchIvSpace;
+ private int mSubsampleEncryptionDataSize;
+ private int[] mScratchSubsampleEncryptedBytesCount;
+ private int[] mScratchSubsampleClearBytesCount;
private boolean mHasSubsampleEncryptionData;
- private CryptoInfo.Pattern mEncryptionPattern;
private int mSkippedSupplementalDataBytes;
private TrackOutputAdapter(int trackIndex) {
mTrackIndex = trackIndex;
- mCryptoInfo = new CryptoInfo();
- mCryptoInfo.iv = new byte[16]; // Size documented in CryptoInfo.
- mCryptoInfo.numBytesOfClearData = new int[0];
- mCryptoInfo.numBytesOfEncryptedData = new int[0];
+ mScratchIvSpace = new byte[16]; // Size documented in CryptoInfo.
+ mScratchSubsampleEncryptedBytesCount = new int[32];
+ mScratchSubsampleClearBytesCount = new int[32];
mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
- mEncryptionPattern =
+ mLastOutputEncryptionPattern =
new CryptoInfo.Pattern(/* blocksToEncrypt= */ 0, /* blocksToSkip= */ 0);
}
@@ -1466,35 +1471,39 @@ public final class MediaParser {
mEncryptionDataReadState = STATE_READING_INIT_VECTOR;
break;
case STATE_READING_INIT_VECTOR:
- Arrays.fill(mCryptoInfo.iv, (byte) 0); // Ensure 0-padding.
- data.readBytes(mCryptoInfo.iv, /* offset= */ 0, mEncryptionVectorSize);
+ Arrays.fill(mScratchIvSpace, (byte) 0); // Ensure 0-padding.
+ data.readBytes(mScratchIvSpace, /* offset= */ 0, mEncryptionVectorSize);
length -= mEncryptionVectorSize;
if (mHasSubsampleEncryptionData) {
mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE;
} else {
- mCryptoInfo.numSubSamples = 0;
+ mSubsampleEncryptionDataSize = 0;
mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
}
break;
case STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE:
- int numSubSamples = data.readUnsignedShort();
- mCryptoInfo.numSubSamples = numSubSamples;
- if (mCryptoInfo.numBytesOfClearData.length < numSubSamples) {
- mCryptoInfo.numBytesOfClearData = new int[numSubSamples];
- mCryptoInfo.numBytesOfEncryptedData = new int[numSubSamples];
+ mSubsampleEncryptionDataSize = data.readUnsignedShort();
+ if (mScratchSubsampleClearBytesCount.length
+ < mSubsampleEncryptionDataSize) {
+ mScratchSubsampleClearBytesCount =
+ new int[mSubsampleEncryptionDataSize];
+ mScratchSubsampleEncryptedBytesCount =
+ new int[mSubsampleEncryptionDataSize];
}
length -= 2;
mEncryptionDataSizeToSubtractFromSampleDataSize +=
- 2 + numSubSamples * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY;
+ 2
+ + mSubsampleEncryptionDataSize
+ * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY;
mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_DATA;
break;
case STATE_READING_SUBSAMPLE_ENCRYPTION_DATA:
- for (int i = 0; i < mCryptoInfo.numSubSamples; i++) {
- mCryptoInfo.numBytesOfClearData[i] = data.readUnsignedShort();
- mCryptoInfo.numBytesOfEncryptedData[i] = data.readInt();
+ for (int i = 0; i < mSubsampleEncryptionDataSize; i++) {
+ mScratchSubsampleClearBytesCount[i] = data.readUnsignedShort();
+ mScratchSubsampleEncryptedBytesCount[i] = data.readInt();
}
length -=
- mCryptoInfo.numSubSamples
+ mSubsampleEncryptionDataSize
* BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY;
mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
if (length != 0) {
@@ -1536,24 +1545,71 @@ public final class MediaParser {
if (cryptoData == null) {
// The sample is not encrypted.
return null;
+ } else if (mInBandCryptoInfo) {
+ if (cryptoData != mLastReceivedCryptoData) {
+ mLastOutputCryptoInfo =
+ createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
+ }
+ } else /* We must populate the full CryptoInfo. */ {
+ // CryptoInfo.pattern is not accessible to the user, so the user needs to feed
+ // this CryptoInfo directly to MediaCodec. We need to create a new CryptoInfo per
+ // sample because of per-sample initialization vector changes.
+ CryptoInfo newCryptoInfo = createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
+ newCryptoInfo.iv = Arrays.copyOf(mScratchIvSpace, mScratchIvSpace.length);
+ boolean canReuseSubsampleInfo =
+ mLastOutputCryptoInfo != null
+ && mLastOutputCryptoInfo.numSubSamples
+ == mSubsampleEncryptionDataSize;
+ for (int i = 0; i < mSubsampleEncryptionDataSize && canReuseSubsampleInfo; i++) {
+ canReuseSubsampleInfo =
+ mLastOutputCryptoInfo.numBytesOfClearData[i]
+ == mScratchSubsampleClearBytesCount[i]
+ && mLastOutputCryptoInfo.numBytesOfEncryptedData[i]
+ == mScratchSubsampleEncryptedBytesCount[i];
+ }
+ newCryptoInfo.numSubSamples = mSubsampleEncryptionDataSize;
+ if (canReuseSubsampleInfo) {
+ newCryptoInfo.numBytesOfClearData = mLastOutputCryptoInfo.numBytesOfClearData;
+ newCryptoInfo.numBytesOfEncryptedData =
+ mLastOutputCryptoInfo.numBytesOfEncryptedData;
+ } else {
+ newCryptoInfo.numBytesOfClearData =
+ Arrays.copyOf(
+ mScratchSubsampleClearBytesCount, mSubsampleEncryptionDataSize);
+ newCryptoInfo.numBytesOfEncryptedData =
+ Arrays.copyOf(
+ mScratchSubsampleEncryptedBytesCount,
+ mSubsampleEncryptionDataSize);
+ }
+ mLastOutputCryptoInfo = newCryptoInfo;
}
- mCryptoInfo.key = cryptoData.encryptionKey;
- // ExoPlayer modes match MediaCodec modes.
- mCryptoInfo.mode = cryptoData.cryptoMode;
- if (cryptoData.clearBlocks != 0) {
- // Content is pattern-encrypted.
- mCryptoInfo.setPattern(mEncryptionPattern);
- mEncryptionPattern.set(cryptoData.encryptedBlocks, cryptoData.clearBlocks);
- } else {
- mCryptoInfo.setPattern(null);
+ mLastReceivedCryptoData = cryptoData;
+ return mLastOutputCryptoInfo;
+ }
+
+ private CryptoInfo createNewCryptoInfoAndPopulateWithCryptoData(CryptoData cryptoData) {
+ CryptoInfo cryptoInfo = new CryptoInfo();
+ cryptoInfo.key = cryptoData.encryptionKey;
+ cryptoInfo.mode = cryptoData.cryptoMode;
+ if (cryptoData.clearBlocks != mLastOutputEncryptionPattern.getSkipBlocks()
+ || cryptoData.encryptedBlocks
+ != mLastOutputEncryptionPattern.getEncryptBlocks()) {
+ mLastOutputEncryptionPattern =
+ new CryptoInfo.Pattern(cryptoData.encryptedBlocks, cryptoData.clearBlocks);
}
- return mCryptoInfo;
+ cryptoInfo.setPattern(mLastOutputEncryptionPattern);
+ return cryptoInfo;
}
private void outputSampleData(ParsableByteArray data, int length) {
mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
try {
- mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchParsableByteArrayAdapter);
+ // Read all bytes from data. ExoPlayer extractors expect all sample data to be
+ // consumed by TrackOutput implementations when passing a ParsableByteArray.
+ while (mScratchParsableByteArrayAdapter.getLength() > 0) {
+ mOutputConsumer.onSampleDataFound(
+ mTrackIndex, mScratchParsableByteArrayAdapter);
+ }
} catch (IOException e) {
// Unexpected.
throw new RuntimeException(e);