Merge pull request #1015 from kamaroyl:feat/PsshVersion1
PiperOrigin-RevId: 603016920
This commit is contained in:
commit
dfcc2cb41d
@ -72,7 +72,9 @@ public final class PsshAtomUtil {
|
|||||||
if (data != null && data.length != 0) {
|
if (data != null && data.length != 0) {
|
||||||
psshBox.putInt(data.length);
|
psshBox.putInt(data.length);
|
||||||
psshBox.put(data);
|
psshBox.put(data);
|
||||||
} // Else the last 4 bytes are a 0 DataSize.
|
} else {
|
||||||
|
psshBox.putInt(0);
|
||||||
|
}
|
||||||
return psshBox.array();
|
return psshBox.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,23 +154,25 @@ public final class PsshAtomUtil {
|
|||||||
* @return The parsed PSSH atom. Null if the input is not a valid PSSH atom, or if the PSSH atom
|
* @return The parsed PSSH atom. Null if the input is not a valid PSSH atom, or if the PSSH atom
|
||||||
* has an unsupported version.
|
* has an unsupported version.
|
||||||
*/
|
*/
|
||||||
// TODO: Support parsing of the key ids for version 1 PSSH atoms.
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static PsshAtom parsePsshAtom(byte[] atom) {
|
public 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.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
atomData.setPosition(0);
|
atomData.setPosition(0);
|
||||||
|
int bufferLength = atomData.bytesLeft();
|
||||||
int atomSize = atomData.readInt();
|
int atomSize = atomData.readInt();
|
||||||
if (atomSize != atomData.bytesLeft() + 4) {
|
if (atomSize != bufferLength) {
|
||||||
// Not an atom, or incorrect atom size.
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"Advertised atom size (" + atomSize + ") does not match buffer size: " + bufferLength);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int atomType = atomData.readInt();
|
int atomType = atomData.readInt();
|
||||||
if (atomType != Atom.TYPE_pssh) {
|
if (atomType != Atom.TYPE_pssh) {
|
||||||
// Not an atom, or incorrect atom type.
|
Log.w(TAG, "Atom type is not pssh: " + atomType);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int atomVersion = Atom.parseFullAtomVersion(atomData.readInt());
|
int atomVersion = Atom.parseFullAtomVersion(atomData.readInt());
|
||||||
@ -177,31 +181,46 @@ public final class PsshAtomUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
UUID uuid = new UUID(atomData.readLong(), atomData.readLong());
|
UUID uuid = new UUID(atomData.readLong(), atomData.readLong());
|
||||||
|
UUID[] keyIds = null;
|
||||||
if (atomVersion == 1) {
|
if (atomVersion == 1) {
|
||||||
int keyIdCount = atomData.readUnsignedIntToInt();
|
int keyIdCount = atomData.readUnsignedIntToInt();
|
||||||
atomData.skipBytes(16 * keyIdCount);
|
keyIds = new UUID[keyIdCount];
|
||||||
|
for (int i = 0; i < keyIdCount; ++i) {
|
||||||
|
keyIds[i] = new UUID(atomData.readLong(), atomData.readLong());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int dataSize = atomData.readUnsignedIntToInt();
|
int dataSize = atomData.readUnsignedIntToInt();
|
||||||
if (dataSize != atomData.bytesLeft()) {
|
bufferLength = atomData.bytesLeft();
|
||||||
// Incorrect dataSize.
|
if (dataSize != bufferLength) {
|
||||||
|
Log.w(
|
||||||
|
TAG, "Atom data size (" + dataSize + ") does not match the bytes left: " + bufferLength);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
byte[] data = new byte[dataSize];
|
byte[] data = new byte[dataSize];
|
||||||
atomData.readBytes(data, 0, dataSize);
|
atomData.readBytes(data, 0, dataSize);
|
||||||
return new PsshAtom(uuid, atomVersion, data);
|
return new PsshAtom(uuid, atomVersion, data, keyIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Consider exposing this and making parsePsshAtom public.
|
/** A class representing the mp4 PSSH Atom as specified in ISO/IEC 23001-7. */
|
||||||
private static class PsshAtom {
|
public static final class PsshAtom {
|
||||||
|
|
||||||
private final UUID uuid;
|
/** The UUID of the encryption system as specified in ISO/IEC 23009-1 section 5.8.4.1. */
|
||||||
private final int version;
|
public final UUID uuid;
|
||||||
private final byte[] schemeData;
|
|
||||||
|
|
||||||
public PsshAtom(UUID uuid, int version, byte[] schemeData) {
|
/** The version of the PSSH atom, either 0 or 1. */
|
||||||
|
public final int version;
|
||||||
|
|
||||||
|
/** Binary scheme data. */
|
||||||
|
public final byte[] schemeData;
|
||||||
|
|
||||||
|
/** Array of key IDs. Always null for version 0 and non-null for version 1. */
|
||||||
|
@Nullable public final UUID[] keyIds;
|
||||||
|
|
||||||
|
/* package */ PsshAtom(UUID uuid, int version, byte[] schemeData, @Nullable UUID[] keyIds) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.schemeData = schemeData;
|
this.schemeData = schemeData;
|
||||||
|
this.keyIds = keyIds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,11 @@ import org.junit.runner.RunWith;
|
|||||||
public final class PsshAtomUtilTest {
|
public final class PsshAtomUtilTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void buildPsshAtom() {
|
public void buildPsshAtom_version0_returnsCorrectBytes() {
|
||||||
byte[] schemeData = new byte[] {0, 1, 2, 3, 4, 5};
|
byte[] schemeData = new byte[] {0, 1, 2, 3, 4, 5};
|
||||||
|
|
||||||
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(C.WIDEVINE_UUID, schemeData);
|
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(C.WIDEVINE_UUID, schemeData);
|
||||||
// Read the PSSH atom back and assert its content is as expected.
|
|
||||||
ParsableByteArray parsablePsshAtom = new ParsableByteArray(psshAtom);
|
ParsableByteArray parsablePsshAtom = new ParsableByteArray(psshAtom);
|
||||||
assertThat(parsablePsshAtom.readUnsignedIntToInt()).isEqualTo(psshAtom.length); // length
|
assertThat(parsablePsshAtom.readUnsignedIntToInt()).isEqualTo(psshAtom.length); // length
|
||||||
assertThat(parsablePsshAtom.readInt()).isEqualTo(TYPE_pssh); // type
|
assertThat(parsablePsshAtom.readInt()).isEqualTo(TYPE_pssh); // type
|
||||||
@ -50,4 +51,208 @@ public final class PsshAtomUtilTest {
|
|||||||
parsablePsshAtom.readBytes(psshSchemeData, 0, schemeData.length);
|
parsablePsshAtom.readBytes(psshSchemeData, 0, schemeData.length);
|
||||||
assertThat(psshSchemeData).isEqualTo(schemeData);
|
assertThat(psshSchemeData).isEqualTo(schemeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildPsshAtom_version1_returnsCorrectBytes() {
|
||||||
|
byte[] schemeData = new byte[] {0, 1, 2, 3, 4, 5};
|
||||||
|
UUID[] keyIds = new UUID[2];
|
||||||
|
keyIds[0] = UUID.fromString("edef8ba9-79d6-4ace-a3c8-27dcd51d21ed");
|
||||||
|
keyIds[1] = UUID.fromString("dc03d7f3-334d-b858-f114-9ab759a925fb");
|
||||||
|
|
||||||
|
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(C.WIDEVINE_UUID, keyIds, schemeData);
|
||||||
|
|
||||||
|
ParsableByteArray parsablePsshAtom = new ParsableByteArray(psshAtom);
|
||||||
|
assertThat(parsablePsshAtom.readUnsignedIntToInt()).isEqualTo(psshAtom.length);
|
||||||
|
assertThat(parsablePsshAtom.readInt()).isEqualTo(TYPE_pssh); // type
|
||||||
|
int fullAtomInt = parsablePsshAtom.readInt(); // version + flags
|
||||||
|
assertThat(parseFullAtomVersion(fullAtomInt)).isEqualTo(1);
|
||||||
|
assertThat(parseFullAtomFlags(fullAtomInt)).isEqualTo(0);
|
||||||
|
UUID systemId = new UUID(parsablePsshAtom.readLong(), parsablePsshAtom.readLong());
|
||||||
|
assertThat(systemId).isEqualTo(WIDEVINE_UUID);
|
||||||
|
assertThat(parsablePsshAtom.readUnsignedIntToInt()).isEqualTo(2);
|
||||||
|
UUID keyId0 = new UUID(parsablePsshAtom.readLong(), parsablePsshAtom.readLong());
|
||||||
|
assertThat(keyId0).isEqualTo(keyIds[0]);
|
||||||
|
UUID keyId1 = new UUID(parsablePsshAtom.readLong(), parsablePsshAtom.readLong());
|
||||||
|
assertThat(keyId1).isEqualTo(keyIds[1]);
|
||||||
|
assertThat(parsablePsshAtom.readUnsignedIntToInt()).isEqualTo(schemeData.length);
|
||||||
|
byte[] psshSchemeData = new byte[schemeData.length];
|
||||||
|
parsablePsshAtom.readBytes(psshSchemeData, 0, schemeData.length);
|
||||||
|
assertThat(psshSchemeData).isEqualTo(schemeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsePsshAtom_version0_parsesCorrectData() {
|
||||||
|
byte[] psshBuffer = {
|
||||||
|
// BMFF box header (36 bytes, 'pssh')
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x24,
|
||||||
|
0x70,
|
||||||
|
0x73,
|
||||||
|
0x73,
|
||||||
|
0x68,
|
||||||
|
// Full box header (version = 0, flags = 0)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
// SystemID
|
||||||
|
0x10,
|
||||||
|
0x77,
|
||||||
|
-0x11,
|
||||||
|
-0x14,
|
||||||
|
-0x40,
|
||||||
|
-0x4e,
|
||||||
|
0x4d,
|
||||||
|
0x02,
|
||||||
|
-0x54,
|
||||||
|
-0x1d,
|
||||||
|
0x3c,
|
||||||
|
0x1e,
|
||||||
|
0x52,
|
||||||
|
-0x1e,
|
||||||
|
-0x05,
|
||||||
|
0x4b,
|
||||||
|
// Size of Data (4)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
// Data bytes
|
||||||
|
0x1a,
|
||||||
|
0x1b,
|
||||||
|
0x1c,
|
||||||
|
0x1d
|
||||||
|
};
|
||||||
|
|
||||||
|
PsshAtomUtil.PsshAtom psshAtom = PsshAtomUtil.parsePsshAtom(psshBuffer);
|
||||||
|
|
||||||
|
assertThat(psshAtom).isNotNull();
|
||||||
|
assertThat(psshAtom.version).isEqualTo(0);
|
||||||
|
assertThat(psshAtom.uuid).isEqualTo(UUID.fromString("1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"));
|
||||||
|
assertThat(psshAtom.keyIds).isNull();
|
||||||
|
assertThat(psshAtom.schemeData).isEqualTo(new byte[] {0x1a, 0x1b, 0x1c, 0x1d});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsePsshAtom_version1_parsesCorrectData() {
|
||||||
|
byte[] psshBuffer = {
|
||||||
|
// BMFF box header (68 bytes, 'pssh')
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x44,
|
||||||
|
0x70,
|
||||||
|
0x73,
|
||||||
|
0x73,
|
||||||
|
0x68,
|
||||||
|
// Full box header (version = 1, flags = 0)
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
// SystemID
|
||||||
|
0x10,
|
||||||
|
0x77,
|
||||||
|
-0x11,
|
||||||
|
-0x14,
|
||||||
|
-0x40,
|
||||||
|
-0x4e,
|
||||||
|
0x4d,
|
||||||
|
0x02,
|
||||||
|
-0x54,
|
||||||
|
-0x1d,
|
||||||
|
0x3c,
|
||||||
|
0x1e,
|
||||||
|
0x52,
|
||||||
|
-0x1e,
|
||||||
|
-0x05,
|
||||||
|
0x4b,
|
||||||
|
// KID_count (2)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
// First KID ("0123456789012345")
|
||||||
|
0x30,
|
||||||
|
0x31,
|
||||||
|
0x32,
|
||||||
|
0x33,
|
||||||
|
0x34,
|
||||||
|
0x35,
|
||||||
|
0x36,
|
||||||
|
0x37,
|
||||||
|
0x38,
|
||||||
|
0x39,
|
||||||
|
0x30,
|
||||||
|
0x31,
|
||||||
|
0x32,
|
||||||
|
0x33,
|
||||||
|
0x34,
|
||||||
|
0x35,
|
||||||
|
// Second KID ("ABCDEFGHIJKLMNOP")
|
||||||
|
0x41,
|
||||||
|
0x42,
|
||||||
|
0x43,
|
||||||
|
0x44,
|
||||||
|
0x45,
|
||||||
|
0x46,
|
||||||
|
0x47,
|
||||||
|
0x48,
|
||||||
|
0x49,
|
||||||
|
0x4a,
|
||||||
|
0x4b,
|
||||||
|
0x4c,
|
||||||
|
0x4d,
|
||||||
|
0x4e,
|
||||||
|
0x4f,
|
||||||
|
0x50,
|
||||||
|
// Size of Data (0)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
PsshAtomUtil.PsshAtom psshAtom = PsshAtomUtil.parsePsshAtom(psshBuffer);
|
||||||
|
|
||||||
|
assertThat(psshAtom).isNotNull();
|
||||||
|
assertThat(psshAtom.version).isEqualTo(1);
|
||||||
|
assertThat(psshAtom.uuid).isEqualTo(UUID.fromString("1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"));
|
||||||
|
assertThat(psshAtom.keyIds).isNotNull();
|
||||||
|
assertThat(psshAtom.keyIds).hasLength(2);
|
||||||
|
assertThat(psshAtom.schemeData).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsePsshAtom_version0FromBuildPsshAtom_returnsEqualData() {
|
||||||
|
byte[] schemeData = new byte[] {0, 1, 2, 3, 4, 5};
|
||||||
|
|
||||||
|
PsshAtomUtil.PsshAtom parsedAtom =
|
||||||
|
PsshAtomUtil.parsePsshAtom(PsshAtomUtil.buildPsshAtom(C.WIDEVINE_UUID, schemeData));
|
||||||
|
|
||||||
|
assertThat(parsedAtom).isNotNull();
|
||||||
|
assertThat(parsedAtom.version).isEqualTo(0);
|
||||||
|
assertThat(parsedAtom.keyIds).isNull();
|
||||||
|
assertThat(parsedAtom.uuid).isEqualTo(C.WIDEVINE_UUID);
|
||||||
|
assertThat(parsedAtom.schemeData).isEqualTo(new byte[] {0, 1, 2, 3, 4, 5});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsePsshAtom_version1FromBuildPsshAtom_returnsEqualData() {
|
||||||
|
byte[] schemeData = new byte[] {0, 1, 2, 3, 4, 5};
|
||||||
|
UUID[] keyIds = new UUID[2];
|
||||||
|
keyIds[0] = UUID.fromString("edef8ba9-79d6-4ace-a3c8-27dcd51d21ed");
|
||||||
|
keyIds[1] = UUID.fromString("dc03d7f3-334d-b858-f114-9ab759a925fb");
|
||||||
|
|
||||||
|
PsshAtomUtil.PsshAtom parsedAtom =
|
||||||
|
PsshAtomUtil.parsePsshAtom(PsshAtomUtil.buildPsshAtom(C.WIDEVINE_UUID, keyIds, schemeData));
|
||||||
|
|
||||||
|
assertThat(parsedAtom).isNotNull();
|
||||||
|
assertThat(parsedAtom.version).isEqualTo(1);
|
||||||
|
assertThat(parsedAtom.keyIds).isEqualTo(keyIds);
|
||||||
|
assertThat(parsedAtom.uuid).isEqualTo(C.WIDEVINE_UUID);
|
||||||
|
assertThat(parsedAtom.schemeData).isEqualTo(new byte[] {0, 1, 2, 3, 4, 5});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user