From 072788c88aa7f53ec2b671d7b95efd712fe0e2d8 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 8 Aug 2017 07:36:31 -0700 Subject: [PATCH] Support building of version 1 PSSH atoms Issue: #3138 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164590835 --- .../extractor/mp4/PsshAtomUtilTest.java | 47 +++++++++++++++++++ .../extractor/mp4/PsshAtomUtil.java | 45 ++++++++++++++---- 2 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtilTest.java diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtilTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtilTest.java new file mode 100644 index 0000000000..5ac3979746 --- /dev/null +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtilTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.extractor.mp4; + +import android.test.MoreAsserts; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.util.ParsableByteArray; +import java.util.UUID; +import junit.framework.TestCase; + +/** + * Tests for {@link PsshAtomUtil}. + */ +public class PsshAtomUtilTest extends TestCase { + + public void testBuildPsshAtom() { + byte[] schemeData = new byte[]{0, 1, 2, 3, 4, 5}; + 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); + assertEquals(psshAtom.length, parsablePsshAtom.readUnsignedIntToInt()); // length + assertEquals(Atom.TYPE_pssh, parsablePsshAtom.readInt()); // type + int fullAtomInt = parsablePsshAtom.readInt(); // version + flags + assertEquals(0, Atom.parseFullAtomVersion(fullAtomInt)); + assertEquals(0, Atom.parseFullAtomFlags(fullAtomInt)); + UUID systemId = new UUID(parsablePsshAtom.readLong(), parsablePsshAtom.readLong()); + assertEquals(C.WIDEVINE_UUID, systemId); + assertEquals(schemeData.length, parsablePsshAtom.readUnsignedIntToInt()); + byte[] psshSchemeData = new byte[schemeData.length]; + parsablePsshAtom.readBytes(psshSchemeData, 0, schemeData.length); + MoreAsserts.assertEquals(schemeData, psshSchemeData); + } + +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java index 6d5c372619..cfca015348 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java @@ -31,22 +31,48 @@ public final class PsshAtomUtil { private PsshAtomUtil() {} /** - * Builds a PSSH atom for a given {@link UUID} containing the given scheme specific data. + * Builds a version 0 PSSH atom for a given system id, containing the given data. * - * @param uuid The UUID of the scheme. + * @param systemId The system id of the scheme. * @param data The scheme specific data. * @return The PSSH atom. */ - public static byte[] buildPsshAtom(UUID uuid, byte[] data) { - int psshBoxLength = Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */ + data.length; + public static byte[] buildPsshAtom(UUID systemId, byte[] data) { + return buildPsshAtom(systemId, null, data); + } + + /** + * Builds a PSSH atom for the given system id, containing the given key ids and data. + * + * @param systemId The system id of the scheme. + * @param keyIds The key ids for a version 1 PSSH atom, or null for a version 0 PSSH atom. + * @param data The scheme specific data. + * @return The PSSH atom. + */ + public static byte[] buildPsshAtom(UUID systemId, UUID[] keyIds, byte[] data) { + boolean buildV1Atom = keyIds != null; + int dataLength = data != null ? data.length : 0; + int psshBoxLength = Atom.FULL_HEADER_SIZE + 16 /* SystemId */ + 4 /* DataSize */ + dataLength; + if (buildV1Atom) { + psshBoxLength += 4 /* KID_count */ + (keyIds.length * 16) /* KIDs */; + } ByteBuffer psshBox = ByteBuffer.allocate(psshBoxLength); psshBox.putInt(psshBoxLength); psshBox.putInt(Atom.TYPE_pssh); - psshBox.putInt(0 /* version=0, flags=0 */); - psshBox.putLong(uuid.getMostSignificantBits()); - psshBox.putLong(uuid.getLeastSignificantBits()); - psshBox.putInt(data.length); - psshBox.put(data); + psshBox.putInt(buildV1Atom ? 0x01000000 : 0 /* version=(buildV1Atom ? 1 : 0), flags=0 */); + psshBox.putLong(systemId.getMostSignificantBits()); + psshBox.putLong(systemId.getLeastSignificantBits()); + if (buildV1Atom) { + psshBox.putInt(keyIds.length); + for (UUID keyId : keyIds) { + psshBox.putLong(keyId.getMostSignificantBits()); + psshBox.putLong(keyId.getLeastSignificantBits()); + } + } + if (dataLength != 0) { + psshBox.putInt(data.length); + psshBox.put(data); + } // Else the last 4 bytes are a 0 DataSize. return psshBox.array(); } @@ -98,6 +124,7 @@ public final class PsshAtomUtil { * @return A pair consisting of the parsed UUID and scheme specific data. Null if the input is * 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. private static Pair parsePsshAtom(byte[] atom) { ParsableByteArray atomData = new ParsableByteArray(atom); if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {