mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Fix handling of encrypted media if IV changes.
1. Correctly replace the AES data source if IV changes. 2. Check the largest timestamp for being equal to MIN_VALUE, and handle this case properly. 3. Clean up AES data source a little. Issue: #162
This commit is contained in:
parent
81e2c9f0d3
commit
03e859d774
@ -67,7 +67,9 @@ public class HlsChunkSource {
|
||||
|
||||
private int variantIndex;
|
||||
private DataSource encryptedDataSource;
|
||||
private String encryptionKeyUri;
|
||||
private Uri encryptionKeyUri;
|
||||
private String encryptedDataSourceIv;
|
||||
private byte[] encryptedDataSourceSecretKey;
|
||||
|
||||
/**
|
||||
* @param dataSource A {@link DataSource} suitable for loading the media data.
|
||||
@ -179,16 +181,17 @@ public class HlsChunkSource {
|
||||
|
||||
// Check if encryption is specified.
|
||||
if (HlsMediaPlaylist.ENCRYPTION_METHOD_AES_128.equals(segment.encryptionMethod)) {
|
||||
if (!segment.encryptionKeyUri.equals(encryptionKeyUri)) {
|
||||
Uri keyUri = Util.getMergedUri(mediaPlaylist.baseUri, segment.encryptionKeyUri);
|
||||
if (!keyUri.equals(encryptionKeyUri)) {
|
||||
// Encryption is specified and the key has changed.
|
||||
Uri keyUri = Util.getMergedUri(mediaPlaylist.baseUri, segment.encryptionKeyUri);
|
||||
HlsChunk toReturn = newEncryptionKeyChunk(keyUri, segment.encryptionIV);
|
||||
encryptionKeyUri = segment.encryptionKeyUri;
|
||||
return toReturn;
|
||||
}
|
||||
if (!Util.areEqual(segment.encryptionIV, encryptedDataSourceIv)) {
|
||||
initEncryptedDataSource(keyUri, segment.encryptionIV, encryptedDataSourceSecretKey);
|
||||
}
|
||||
} else {
|
||||
encryptedDataSource = null;
|
||||
encryptionKeyUri = null;
|
||||
clearEncryptedDataSource();
|
||||
}
|
||||
|
||||
long startTimeUs;
|
||||
@ -290,6 +293,33 @@ public class HlsChunkSource {
|
||||
return new EncryptionKeyChunk(upstreamDataSource, dataSpec, iv);
|
||||
}
|
||||
|
||||
/* package */ void initEncryptedDataSource(Uri keyUri, String iv, byte[] secretKey) {
|
||||
String trimmedIv;
|
||||
if (iv.toLowerCase(Locale.getDefault()).startsWith("0x")) {
|
||||
trimmedIv = iv.substring(2);
|
||||
} else {
|
||||
trimmedIv = iv;
|
||||
}
|
||||
|
||||
byte[] ivData = new BigInteger(trimmedIv, 16).toByteArray();
|
||||
byte[] ivDataWithPadding = new byte[16];
|
||||
int offset = ivData.length > 16 ? ivData.length - 16 : 0;
|
||||
System.arraycopy(ivData, offset, ivDataWithPadding, ivDataWithPadding.length - ivData.length
|
||||
+ offset, ivData.length - offset);
|
||||
|
||||
encryptedDataSource = new Aes128DataSource(secretKey, ivDataWithPadding, upstreamDataSource);
|
||||
encryptionKeyUri = keyUri;
|
||||
encryptedDataSourceIv = iv;
|
||||
encryptedDataSourceSecretKey = secretKey;
|
||||
}
|
||||
|
||||
private void clearEncryptedDataSource() {
|
||||
encryptionKeyUri = null;
|
||||
encryptedDataSource = null;
|
||||
encryptedDataSourceIv = null;
|
||||
encryptedDataSourceSecretKey = null;
|
||||
}
|
||||
|
||||
private static Variant[] filterVariants(HlsMasterPlaylist masterPlaylist, int[] variantIndices) {
|
||||
List<Variant> masterVariants = masterPlaylist.variants;
|
||||
ArrayList<Variant> enabledVariants = new ArrayList<Variant>();
|
||||
@ -378,25 +408,14 @@ public class HlsChunkSource {
|
||||
|
||||
public EncryptionKeyChunk(DataSource dataSource, DataSpec dataSpec, String iv) {
|
||||
super(dataSource, dataSpec, bitArray);
|
||||
if (iv.toLowerCase(Locale.getDefault()).startsWith("0x")) {
|
||||
this.iv = iv.substring(2);
|
||||
} else {
|
||||
this.iv = iv;
|
||||
}
|
||||
this.iv = iv;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void consume(BitArray data) throws IOException {
|
||||
byte[] secretKey = new byte[data.bytesLeft()];
|
||||
data.readBytes(secretKey, 0, secretKey.length);
|
||||
|
||||
byte[] ivData = new BigInteger(iv, 16).toByteArray();
|
||||
byte[] ivDataWithPadding = new byte[16];
|
||||
int offset = ivData.length > 16 ? ivData.length - 16 : 0;
|
||||
System.arraycopy(ivData, offset, ivDataWithPadding, ivDataWithPadding.length - ivData.length
|
||||
+ offset, ivData.length - offset);
|
||||
|
||||
encryptedDataSource = new Aes128DataSource(secretKey, ivDataWithPadding, upstreamDataSource);
|
||||
initEncryptedDataSource(dataSpec.uri, iv, secretKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -256,7 +256,9 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
} else if (loadingFinished) {
|
||||
return TrackRenderer.END_OF_TRACK_US;
|
||||
} else {
|
||||
return extractors.getLast().getLargestSampleTimestamp();
|
||||
long largestSampleTimestamp = extractors.getLast().getLargestSampleTimestamp();
|
||||
return largestSampleTimestamp == Long.MIN_VALUE ? downstreamPositionUs
|
||||
: largestSampleTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,8 +351,13 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean bufferFull = !extractors.isEmpty() && (extractors.getLast().getLargestSampleTimestamp()
|
||||
- downstreamPositionUs) >= BUFFER_DURATION_US;
|
||||
boolean bufferFull = false;
|
||||
if (!extractors.isEmpty()) {
|
||||
long largestSampleTimestamp = extractors.getLast().getLargestSampleTimestamp();
|
||||
bufferFull = largestSampleTimestamp != Long.MIN_VALUE
|
||||
&& (largestSampleTimestamp - downstreamPositionUs) >= BUFFER_DURATION_US;
|
||||
}
|
||||
|
||||
if (loader.isLoading() || bufferFull) {
|
||||
return;
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ public class Aes128DataSource implements DataSource {
|
||||
private final byte[] secretKey;
|
||||
private final byte[] iv;
|
||||
|
||||
private Cipher cipher;
|
||||
private CipherInputStream cipherInputStream;
|
||||
|
||||
public Aes128DataSource(byte[] secretKey, byte[] iv, DataSource upstream) {
|
||||
@ -53,6 +52,7 @@ public class Aes128DataSource implements DataSource {
|
||||
|
||||
@Override
|
||||
public long open(DataSpec dataSpec) throws IOException {
|
||||
Cipher cipher;
|
||||
try {
|
||||
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
@ -80,6 +80,7 @@ public class Aes128DataSource implements DataSource {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
cipherInputStream = null;
|
||||
upstream.close();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user