Merge pull request #3328 from ValveSoftware/dev-v2-multiversion
Allow multiple PSSH boxes for same system
This commit is contained in:
commit
40d8b29c9e
@ -349,7 +349,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
// If there is no scheme information, assume patternless AES-CTR.
|
// If there is no scheme information, assume patternless AES-CTR.
|
||||||
return true;
|
return true;
|
||||||
} else if (C.CENC_TYPE_cbc1.equals(schemeType) || C.CENC_TYPE_cbcs.equals(schemeType)
|
} else if (C.CENC_TYPE_cbc1.equals(schemeType) || C.CENC_TYPE_cbcs.equals(schemeType)
|
||||||
|| C.CENC_TYPE_cens.equals(schemeType)) {
|
|| C.CENC_TYPE_cens.equals(schemeType)) {
|
||||||
// AES-CBC and pattern encryption are supported on API 24 onwards.
|
// AES-CBC and pattern encryption are supported on API 24 onwards.
|
||||||
return Util.SDK_INT >= 24;
|
return Util.SDK_INT >= 24;
|
||||||
}
|
}
|
||||||
@ -357,6 +357,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
|
public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
|
||||||
Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
|
Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
|
||||||
@ -461,12 +462,34 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
* @return The extracted {@link SchemeData}, or null if no suitable data is present.
|
* @return The extracted {@link SchemeData}, or null if no suitable data is present.
|
||||||
*/
|
*/
|
||||||
private static SchemeData getSchemeData(DrmInitData drmInitData, UUID uuid) {
|
private static SchemeData getSchemeData(DrmInitData drmInitData, UUID uuid) {
|
||||||
SchemeData schemeData = drmInitData.get(uuid);
|
List<SchemeData> schemeDatas = new ArrayList<>();
|
||||||
if (schemeData == null && C.CLEARKEY_UUID.equals(uuid)) {
|
// Look for matching PSSH boxes, or the common box in the case of ClearKey
|
||||||
// If present, the Common PSSH box should be used for ClearKey.
|
for (int i = 0; i < drmInitData.schemeDataCount; ++i) {
|
||||||
schemeData = drmInitData.get(C.COMMON_PSSH_UUID);
|
SchemeData schemeData = drmInitData.get(i);
|
||||||
|
if (schemeData.matches(uuid)
|
||||||
|
|| (C.CLEARKEY_UUID.equals(uuid) && schemeData.matches(C.COMMON_PSSH_UUID))) {
|
||||||
|
schemeDatas.add(schemeData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return schemeData;
|
|
||||||
|
if (schemeDatas.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Widevine, we prefer v1 init data on M and higher, v0 for lower
|
||||||
|
if (C.WIDEVINE_UUID.equals(uuid)) {
|
||||||
|
for (SchemeData schemeData : schemeDatas ) {
|
||||||
|
int version = PsshAtomUtil.parseVersion(schemeData.data);
|
||||||
|
if (Util.SDK_INT < 23 && version == 0) {
|
||||||
|
return schemeData;
|
||||||
|
} else if (Util.SDK_INT >= 23 && version == 1) {
|
||||||
|
return schemeData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have any special handling for this system, we take the first scheme data found
|
||||||
|
return schemeDatas.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] getSchemeInitData(SchemeData data, UUID uuid) {
|
private static byte[] getSchemeInitData(SchemeData data, UUID uuid) {
|
||||||
|
@ -22,6 +22,8 @@ import com.google.android.exoplayer2.C;
|
|||||||
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -78,12 +80,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
// Sorting ensures that universal scheme data (i.e. data that applies to all schemes) is matched
|
// Sorting ensures that universal scheme data (i.e. data that applies to all schemes) is matched
|
||||||
// last. It's also required by the equals and hashcode implementations.
|
// last. It's also required by the equals and hashcode implementations.
|
||||||
Arrays.sort(schemeDatas, this);
|
Arrays.sort(schemeDatas, this);
|
||||||
// Check for no duplicates.
|
|
||||||
for (int i = 1; i < schemeDatas.length; i++) {
|
|
||||||
if (schemeDatas[i - 1].uuid.equals(schemeDatas[i].uuid)) {
|
|
||||||
throw new IllegalArgumentException("Duplicate data for uuid: " + schemeDatas[i].uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.schemeDatas = schemeDatas;
|
this.schemeDatas = schemeDatas;
|
||||||
schemeDataCount = schemeDatas.length;
|
schemeDataCount = schemeDatas.length;
|
||||||
}
|
}
|
||||||
@ -97,9 +94,11 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
/**
|
/**
|
||||||
* Retrieves data for a given DRM scheme, specified by its UUID.
|
* Retrieves data for a given DRM scheme, specified by its UUID.
|
||||||
*
|
*
|
||||||
|
* @deprecated This will only get the first data found for the scheme.
|
||||||
* @param uuid The DRM scheme's UUID.
|
* @param uuid The DRM scheme's UUID.
|
||||||
* @return The initialization data for the scheme, or null if the scheme is not supported.
|
* @return The initialization data for the scheme, or null if the scheme is not supported.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public SchemeData get(UUID uuid) {
|
public SchemeData get(UUID uuid) {
|
||||||
for (SchemeData schemeData : schemeDatas) {
|
for (SchemeData schemeData : schemeDatas) {
|
||||||
if (schemeData.matches(uuid)) {
|
if (schemeData.matches(uuid)) {
|
||||||
@ -112,7 +111,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
/**
|
/**
|
||||||
* Retrieves the {@link SchemeData} at a given index.
|
* Retrieves the {@link SchemeData} at a given index.
|
||||||
*
|
*
|
||||||
* @param index index of the scheme to return.
|
* @param index index of the scheme to return. Must not exceed {@link #schemeDataCount}.
|
||||||
* @return The {@link SchemeData} at the index.
|
* @return The {@link SchemeData} at the index.
|
||||||
*/
|
*/
|
||||||
public SchemeData get(int index) {
|
public SchemeData get(int index) {
|
||||||
|
@ -86,11 +86,28 @@ public final class PsshAtomUtil {
|
|||||||
* an unsupported version.
|
* an unsupported version.
|
||||||
*/
|
*/
|
||||||
public static UUID parseUuid(byte[] atom) {
|
public static UUID parseUuid(byte[] atom) {
|
||||||
Pair<UUID, byte[]> parsedAtom = parsePsshAtom(atom);
|
PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||||
if (parsedAtom == null) {
|
if (parsedAtom == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return parsedAtom.first;
|
return parsedAtom.uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the version from a PSSH atom. Version 0 and 1 PSSH atoms are supported.
|
||||||
|
* <p>
|
||||||
|
* The UUID is only parsed if the data is a valid PSSH atom.
|
||||||
|
*
|
||||||
|
* @param atom The atom to parse.
|
||||||
|
* @return The parsed UUID. -1 if the input is not a valid PSSH atom, or if the PSSH atom has
|
||||||
|
* an unsupported version.
|
||||||
|
*/
|
||||||
|
public static int parseVersion(byte[] atom) {
|
||||||
|
PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||||
|
if (parsedAtom == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return parsedAtom.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,15 +122,15 @@ public final class PsshAtomUtil {
|
|||||||
* PSSH atom has an unsupported version, or if the PSSH atom does not match the passed UUID.
|
* PSSH atom has an unsupported version, or if the PSSH atom does not match the passed UUID.
|
||||||
*/
|
*/
|
||||||
public static byte[] parseSchemeSpecificData(byte[] atom, UUID uuid) {
|
public static byte[] parseSchemeSpecificData(byte[] atom, UUID uuid) {
|
||||||
Pair<UUID, byte[]> parsedAtom = parsePsshAtom(atom);
|
PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||||
if (parsedAtom == null) {
|
if (parsedAtom == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (uuid != null && !uuid.equals(parsedAtom.first)) {
|
if (uuid != null && !uuid.equals(parsedAtom.uuid)) {
|
||||||
Log.w(TAG, "UUID mismatch. Expected: " + uuid + ", got: " + parsedAtom.first + ".");
|
Log.w(TAG, "UUID mismatch. Expected: " + uuid + ", got: " + parsedAtom.uuid + ".");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return parsedAtom.second;
|
return parsedAtom.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,7 +142,7 @@ public final class PsshAtomUtil {
|
|||||||
* not a valid PSSH atom, or if the PSSH atom has an unsupported version.
|
* not a valid PSSH atom, or if the PSSH atom has an unsupported version.
|
||||||
*/
|
*/
|
||||||
// TODO: Support parsing of the key ids for version 1 PSSH atoms.
|
// TODO: Support parsing of the key ids for version 1 PSSH atoms.
|
||||||
private static Pair<UUID, byte[]> parsePsshAtom(byte[] atom) {
|
private static PsshAtom parsePsshAtom(byte[] atom) {
|
||||||
ParsableByteArray atomData = new ParsableByteArray(atom);
|
ParsableByteArray atomData = new ParsableByteArray(atom);
|
||||||
if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
|
if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
|
||||||
// Data too short.
|
// Data too short.
|
||||||
@ -159,7 +176,18 @@ public final class PsshAtomUtil {
|
|||||||
}
|
}
|
||||||
byte[] data = new byte[dataSize];
|
byte[] data = new byte[dataSize];
|
||||||
atomData.readBytes(data, 0, dataSize);
|
atomData.readBytes(data, 0, dataSize);
|
||||||
return Pair.create(uuid, data);
|
return new PsshAtom(uuid, atomVersion, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private static class PsshAtom {
|
||||||
|
final UUID uuid;
|
||||||
|
final int version;
|
||||||
|
final byte[] data;
|
||||||
|
|
||||||
|
PsshAtom(final UUID uuid, final int version, final byte[] data) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.version = version;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,9 @@ import org.junit.runner.RunWith;
|
|||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit test for {@link DrmInitData}.
|
* Unit test for {@link DrmInitData}.
|
||||||
*/
|
*/
|
||||||
@ -97,6 +100,7 @@ public class DrmInitDataTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Deprecated
|
||||||
public void testGet() {
|
public void testGet() {
|
||||||
// Basic matching.
|
// Basic matching.
|
||||||
DrmInitData testInitData = new DrmInitData(DATA_1, DATA_2);
|
DrmInitData testInitData = new DrmInitData(DATA_1, DATA_2);
|
||||||
@ -124,27 +128,22 @@ public class DrmInitDataTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDuplicateSchemeDataRejected() {
|
public void testGetByIndex() {
|
||||||
try {
|
DrmInitData testInitData = new DrmInitData(DATA_1, DATA_2);
|
||||||
new DrmInitData(DATA_1, DATA_1);
|
assertThat(getAllSchemeData(testInitData)).containsAllOf(DATA_1, DATA_2);
|
||||||
fail();
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// Expected.
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
@Test
|
||||||
new DrmInitData(DATA_1, DATA_1B);
|
public void testDuplicateSchemeData() {
|
||||||
fail();
|
DrmInitData testInitData = new DrmInitData(DATA_1, DATA_1);
|
||||||
} catch (IllegalArgumentException e) {
|
assertThat(testInitData.schemeDataCount).isEqualTo(2);
|
||||||
// Expected.
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
testInitData = new DrmInitData(DATA_1, DATA_2, DATA_1B);
|
||||||
new DrmInitData(DATA_1, DATA_2, DATA_1B);
|
assertThat(testInitData.schemeDataCount).isEqualTo(3);
|
||||||
fail();
|
assertThat(getAllSchemeData(testInitData)).containsAllOf(DATA_1, DATA_1B, DATA_2);
|
||||||
} catch (IllegalArgumentException e) {
|
// Deprecated get method should return first entry
|
||||||
// Expected.
|
assertThat(testInitData.get(WIDEVINE_UUID)).isEqualTo(DATA_1);
|
||||||
}
|
assertThat(testInitData.get(PLAYREADY_UUID)).isEqualTo(DATA_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -162,4 +161,12 @@ public class DrmInitDataTest {
|
|||||||
assertThat(DATA_UNIVERSAL.matches(UUID_NIL)).isTrue();
|
assertThat(DATA_UNIVERSAL.matches(UUID_NIL)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<SchemeData> getAllSchemeData(DrmInitData drmInitData) {
|
||||||
|
ArrayList<SchemeData> schemeDatas = new ArrayList<>();
|
||||||
|
for (int i = 0; i < drmInitData.schemeDataCount; i++) {
|
||||||
|
schemeDatas.add(drmInitData.get(i));
|
||||||
|
}
|
||||||
|
return schemeDatas;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user