Merge "Fill crypto data in MediaParser" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b7cc9681ff
@@ -65,6 +65,7 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -709,6 +710,35 @@ public final class MediaParser {
|
|||||||
*/
|
*/
|
||||||
public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS =
|
public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS =
|
||||||
"android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
|
"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.
|
// 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_SINGLE_PMT = "single_pmt";
|
||||||
private static final String TS_MODE_MULTI_PMT = "multi_pmt";
|
private static final String TS_MODE_MULTI_PMT = "multi_pmt";
|
||||||
private static final String TS_MODE_HLS = "hls";
|
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.
|
// Instance creation methods.
|
||||||
|
|
||||||
@@ -853,6 +898,7 @@ public final class MediaParser {
|
|||||||
private final DataReaderAdapter mScratchDataReaderAdapter;
|
private final DataReaderAdapter mScratchDataReaderAdapter;
|
||||||
private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
|
private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
|
||||||
@Nullable private final Constructor<DrmInitData.SchemeInitData> mSchemeInitDataConstructor;
|
@Nullable private final Constructor<DrmInitData.SchemeInitData> mSchemeInitDataConstructor;
|
||||||
|
private boolean mInBandCryptoInfo;
|
||||||
private String mParserName;
|
private String mParserName;
|
||||||
private Extractor mExtractor;
|
private Extractor mExtractor;
|
||||||
private ExtractorInput mExtractorInput;
|
private ExtractorInput mExtractorInput;
|
||||||
@@ -900,6 +946,9 @@ public final class MediaParser {
|
|||||||
&& !TS_MODE_MULTI_PMT.equals(value)) {
|
&& !TS_MODE_MULTI_PMT.equals(value)) {
|
||||||
throw new IllegalArgumentException(PARAMETER_TS_MODE + " does not accept: " + 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);
|
mParserParameters.put(parameterName, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -1274,9 +1323,23 @@ public final class MediaParser {
|
|||||||
private class TrackOutputAdapter implements TrackOutput {
|
private class TrackOutputAdapter implements TrackOutput {
|
||||||
|
|
||||||
private final int mTrackIndex;
|
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) {
|
private TrackOutputAdapter(int trackIndex) {
|
||||||
mTrackIndex = 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
|
@Override
|
||||||
@@ -1303,6 +1366,98 @@ public final class MediaParser {
|
|||||||
@Override
|
@Override
|
||||||
public void sampleData(
|
public void sampleData(
|
||||||
ParsableByteArray data, int length, @SampleDataPart int sampleDataPart) {
|
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);
|
mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
|
||||||
try {
|
try {
|
||||||
mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchParsableByteArrayAdapter);
|
mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchParsableByteArrayAdapter);
|
||||||
@@ -1311,13 +1466,6 @@ public final class MediaParser {
|
|||||||
throw new RuntimeException(e);
|
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 {
|
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}. */
|
/** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */
|
||||||
private static SeekPoint toSeekPoint(
|
private static SeekPoint toSeekPoint(
|
||||||
com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) {
|
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
|
@Nullable
|
||||||
private static Constructor<DrmInitData.SchemeInitData> getSchemeInitDataConstructor() {
|
private static Constructor<DrmInitData.SchemeInitData> getSchemeInitDataConstructor() {
|
||||||
// TODO: Use constructor statically when available.
|
// 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_IGNORE_SPLICE_INFO_STREAM, Boolean.class);
|
||||||
expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, 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_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);
|
EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user