Merge "Fill crypto data in MediaParser" into rvc-dev am: b7cc9681ff am: 2c900aa4f1

Change-Id: I3a61470374a7f582eb8c72c7f51d59b4a14d766f
This commit is contained in:
Santiago Seifert
2020-05-11 14:45:52 +00:00
committed by Automerger Merge Worker

View File

@@ -65,6 +65,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -709,6 +710,35 @@ public final class MediaParser {
*/
public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS =
"android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
/**
* Sets whether encryption data should be sent in-band with the sample data, as per {@link
* OutputConsumer#onSampleDataFound}. {@code boolean} expected. Default value is {@code false}.
*
* <p>If this parameter is set, encrypted samples' data will be prefixed with the encryption
* information bytes. The format for in-band encryption information is:
*
* <ul>
* <li>(1 byte) {@code encryption_signal_byte}: Most significant bit signals whether the
* encryption data contains subsample encryption data. The remaining bits contain {@code
* initialization_vector_size}.
* <li>({@code initialization_vector_size} bytes) Initialization vector.
* <li>If subsample encryption data is present, as per {@code encryption_signal_byte}, the
* encryption data also contains:
* <ul>
* <li>(2 bytes) {@code subsample_encryption_data_length}.
* <li>({@code subsample_encryption_data_length * 6} bytes) Subsample encryption data
* (repeated {@code subsample_encryption_data_length} times):
* <ul>
* <li>(2 bytes) Size of a clear section in sample.
* <li>(4 bytes) Size of an encryption section in sample.
* </ul>
* </ul>
* </ul>
*
* @hide
*/
public static final String PARAMETER_IN_BAND_CRYPTO_INFO =
"android.media.mediaparser.inBandCryptoInfo";
// Private constants.
@@ -718,6 +748,21 @@ public final class MediaParser {
private static final String TS_MODE_SINGLE_PMT = "single_pmt";
private static final String TS_MODE_MULTI_PMT = "multi_pmt";
private static final String TS_MODE_HLS = "hls";
private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6;
@IntDef(
value = {
STATE_READING_SIGNAL_BYTE,
STATE_READING_INIT_VECTOR,
STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE,
STATE_READING_SUBSAMPLE_ENCRYPTION_DATA
})
private @interface EncryptionDataReadState {}
private static final int STATE_READING_SIGNAL_BYTE = 0;
private static final int STATE_READING_INIT_VECTOR = 1;
private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE = 2;
private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_DATA = 3;
// Instance creation methods.
@@ -853,6 +898,7 @@ public final class MediaParser {
private final DataReaderAdapter mScratchDataReaderAdapter;
private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
@Nullable private final Constructor<DrmInitData.SchemeInitData> mSchemeInitDataConstructor;
private boolean mInBandCryptoInfo;
private String mParserName;
private Extractor mExtractor;
private ExtractorInput mExtractorInput;
@@ -900,6 +946,9 @@ public final class MediaParser {
&& !TS_MODE_MULTI_PMT.equals(value)) {
throw new IllegalArgumentException(PARAMETER_TS_MODE + " does not accept: " + value);
}
if (PARAMETER_IN_BAND_CRYPTO_INFO.equals(parameterName)) {
mInBandCryptoInfo = (boolean) value;
}
mParserParameters.put(parameterName, value);
return this;
}
@@ -1274,9 +1323,23 @@ public final class MediaParser {
private class TrackOutputAdapter implements TrackOutput {
private final int mTrackIndex;
private final CryptoInfo mCryptoInfo;
@EncryptionDataReadState private int mEncryptionDataReadState;
private int mEncryptionDataSizeToSubtractFromSampleDataSize;
private int mEncryptionVectorSize;
private boolean mHasSubsampleEncryptionData;
private CryptoInfo.Pattern mEncryptionPattern;
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];
mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
mEncryptionPattern =
new CryptoInfo.Pattern(/* blocksToEncrypt= */ 0, /* blocksToSkip= */ 0);
}
@Override
@@ -1303,6 +1366,98 @@ public final class MediaParser {
@Override
public void sampleData(
ParsableByteArray data, int length, @SampleDataPart int sampleDataPart) {
if (sampleDataPart == SAMPLE_DATA_PART_ENCRYPTION && !mInBandCryptoInfo) {
while (length > 0) {
switch (mEncryptionDataReadState) {
case STATE_READING_SIGNAL_BYTE:
int encryptionSignalByte = data.readUnsignedByte();
length--;
mHasSubsampleEncryptionData = ((encryptionSignalByte >> 7) & 1) != 0;
mEncryptionVectorSize = encryptionSignalByte & 0x7F;
mEncryptionDataSizeToSubtractFromSampleDataSize =
mEncryptionVectorSize + 1; // Signal byte.
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);
length -= mEncryptionVectorSize;
if (mHasSubsampleEncryptionData) {
mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE;
} else {
mCryptoInfo.numSubSamples = 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];
}
length -= 2;
mEncryptionDataSizeToSubtractFromSampleDataSize +=
2 + numSubSamples * 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();
}
length -=
mCryptoInfo.numSubSamples
* BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY;
mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
if (length != 0) {
throw new IllegalStateException();
}
break;
default:
// Never happens.
throw new IllegalStateException();
}
}
} else {
outputSampleData(data, length);
}
}
@Override
public void sampleMetadata(
long timeUs, int flags, int size, int offset, @Nullable CryptoData cryptoData) {
mOutputConsumer.onSampleCompleted(
mTrackIndex,
timeUs,
getMediaParserFlags(flags),
size - mEncryptionDataSizeToSubtractFromSampleDataSize,
offset,
getPopulatedCryptoInfo(cryptoData));
mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
mEncryptionDataSizeToSubtractFromSampleDataSize = 0;
}
@Nullable
private CryptoInfo getPopulatedCryptoInfo(@Nullable CryptoData cryptoData) {
if (cryptoData == null) {
// The sample is not encrypted.
return null;
}
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);
}
return mCryptoInfo;
}
private void outputSampleData(ParsableByteArray data, int length) {
mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
try {
mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchParsableByteArrayAdapter);
@@ -1311,13 +1466,6 @@ public final class MediaParser {
throw new RuntimeException(e);
}
}
@Override
public void sampleMetadata(
long timeUs, int flags, int size, int offset, CryptoData encryptionData) {
mOutputConsumer.onSampleCompleted(
mTrackIndex, timeUs, flags, size, offset, toCryptoInfo(encryptionData));
}
}
private static final class DataReaderAdapter implements InputReader {
@@ -1519,11 +1667,6 @@ public final class MediaParser {
}
}
private static CryptoInfo toCryptoInfo(TrackOutput.CryptoData encryptionData) {
// TODO: Implement.
return null;
}
/** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */
private static SeekPoint toSeekPoint(
com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) {
@@ -1543,6 +1686,19 @@ public final class MediaParser {
}
}
private static int getMediaParserFlags(int flags) {
@SampleFlags int result = 0;
result |= (flags & C.BUFFER_FLAG_ENCRYPTED) != 0 ? SAMPLE_FLAG_ENCRYPTED : 0;
result |= (flags & C.BUFFER_FLAG_KEY_FRAME) != 0 ? SAMPLE_FLAG_KEY_FRAME : 0;
result |= (flags & C.BUFFER_FLAG_DECODE_ONLY) != 0 ? SAMPLE_FLAG_DECODE_ONLY : 0;
result |=
(flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0
? SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA
: 0;
result |= (flags & C.BUFFER_FLAG_LAST_SAMPLE) != 0 ? SAMPLE_FLAG_LAST_SAMPLE : 0;
return result;
}
@Nullable
private static Constructor<DrmInitData.SchemeInitData> getSchemeInitDataConstructor() {
// TODO: Use constructor statically when available.
@@ -1598,6 +1754,7 @@ public final class MediaParser {
expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_IN_BAND_CRYPTO_INFO, Boolean.class);
EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName);
}
}