Commit a1299ea7 by olly Committed by Oliver Woodman

Have DrmInitData classes implement hashCode/equals.

This will be required for key rotation to work, since
we'll need a way to determine whether or not the init
data has changed.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=121018211
parent 9b574878
/*
* Copyright (C) 2014 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.exoplayer.drm;
import static com.google.android.exoplayer.drm.StreamingDrmSessionManager.PLAYREADY_UUID;
import static com.google.android.exoplayer.drm.StreamingDrmSessionManager.WIDEVINE_UUID;
import static com.google.android.exoplayer.util.MimeTypes.VIDEO_MP4;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
import com.google.android.exoplayer.testutil.TestUtil;
import android.test.MoreAsserts;
import junit.framework.TestCase;
/**
* Unit test for {@link DrmInitData}.
*/
public class DrmInitDataTest extends TestCase {
private static final SchemeInitData DATA_1 =
new SchemeInitData(VIDEO_MP4, TestUtil.buildTestData(128, 1 /* data seed */));
private static final SchemeInitData DATA_2 =
new SchemeInitData(VIDEO_MP4, TestUtil.buildTestData(128, 2 /* data seed */));
public void testMappedEquals() {
DrmInitData.Mapped drmInitData = new DrmInitData.Mapped(
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1),
new UuidSchemeInitDataTuple(PLAYREADY_UUID, DATA_2));
// Basic non-referential equality test.
DrmInitData.Mapped testInitData = new DrmInitData.Mapped(
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1),
new UuidSchemeInitDataTuple(PLAYREADY_UUID, DATA_2));
assertEquals(drmInitData, testInitData);
assertEquals(drmInitData.hashCode(), testInitData.hashCode());
// Passing the tuples in reverse order shouldn't affect equality.
testInitData = new DrmInitData.Mapped(
new UuidSchemeInitDataTuple(PLAYREADY_UUID, DATA_2),
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1));
assertEquals(drmInitData, testInitData);
assertEquals(drmInitData.hashCode(), testInitData.hashCode());
// Different number of tuples should affect equality.
testInitData = new DrmInitData.Mapped(
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1));
MoreAsserts.assertNotEqual(drmInitData, testInitData);
// Different data in one of the tuples should affect equality.
testInitData = new DrmInitData.Mapped(
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1),
new UuidSchemeInitDataTuple(PLAYREADY_UUID, DATA_1));
MoreAsserts.assertNotEqual(drmInitData, testInitData);
}
public void testUniversalEquals() {
DrmInitData.Universal drmInitData = new DrmInitData.Universal(DATA_1);
// Basic non-referential equality test.
DrmInitData.Universal testInitData = new DrmInitData.Universal(DATA_1);
assertEquals(drmInitData, testInitData);
assertEquals(drmInitData.hashCode(), testInitData.hashCode());
// Different data should affect equality.
testInitData = new DrmInitData.Universal(DATA_2);
MoreAsserts.assertNotEqual(drmInitData, testInitData);
}
}
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData; import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil; import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
...@@ -286,12 +287,15 @@ public final class FrameworkSampleSource implements SampleSource { ...@@ -286,12 +287,15 @@ public final class FrameworkSampleSource implements SampleSource {
if (psshInfo == null || psshInfo.isEmpty()) { if (psshInfo == null || psshInfo.isEmpty()) {
return null; return null;
} }
DrmInitData.Mapped drmInitData = new DrmInitData.Mapped(); UuidSchemeInitDataTuple[] uuidSchemeInitDataTuples =
new UuidSchemeInitDataTuple[psshInfo.size()];
int count = 0;
for (UUID uuid : psshInfo.keySet()) { for (UUID uuid : psshInfo.keySet()) {
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(uuid, psshInfo.get(uuid)); byte[] psshAtom = PsshAtomUtil.buildPsshAtom(uuid, psshInfo.get(uuid));
drmInitData.put(uuid, new SchemeInitData(MimeTypes.VIDEO_MP4, psshAtom)); SchemeInitData schemeData = new SchemeInitData(MimeTypes.VIDEO_MP4, psshAtom);
uuidSchemeInitDataTuples[count++] = new UuidSchemeInitDataTuple(uuid, schemeData);
} }
return drmInitData; return new DrmInitData.Mapped(uuidSchemeInitDataTuples);
} }
private void seekToUsInternal(long positionUs, boolean force) { private void seekToUsInternal(long positionUs, boolean force) {
......
...@@ -37,6 +37,7 @@ import com.google.android.exoplayer.dash.mpd.Period; ...@@ -37,6 +37,7 @@ import com.google.android.exoplayer.dash.mpd.Period;
import com.google.android.exoplayer.dash.mpd.RangedUri; import com.google.android.exoplayer.dash.mpd.RangedUri;
import com.google.android.exoplayer.dash.mpd.Representation; import com.google.android.exoplayer.dash.mpd.Representation;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
import com.google.android.exoplayer.extractor.ChunkIndex; import com.google.android.exoplayer.extractor.ChunkIndex;
import com.google.android.exoplayer.extractor.SeekMap; import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor;
...@@ -49,6 +50,7 @@ import com.google.android.exoplayer.util.Util; ...@@ -49,6 +50,7 @@ import com.google.android.exoplayer.util.Util;
import android.os.SystemClock; import android.os.SystemClock;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
...@@ -342,17 +344,19 @@ public class DashChunkSource implements ChunkSource { ...@@ -342,17 +344,19 @@ public class DashChunkSource implements ChunkSource {
} }
private static DrmInitData getDrmInitData(AdaptationSet adaptationSet) { private static DrmInitData getDrmInitData(AdaptationSet adaptationSet) {
DrmInitData.Mapped drmInitData = null; ArrayList<UuidSchemeInitDataTuple> uuidSchemeInitDataTuples = null;
for (int i = 0; i < adaptationSet.contentProtections.size(); i++) { for (int i = 0; i < adaptationSet.contentProtections.size(); i++) {
ContentProtection contentProtection = adaptationSet.contentProtections.get(i); ContentProtection contentProtection = adaptationSet.contentProtections.get(i);
if (contentProtection.uuid != null && contentProtection.data != null) { if (contentProtection.uuid != null && contentProtection.data != null) {
if (drmInitData == null) { if (uuidSchemeInitDataTuples == null) {
drmInitData = new DrmInitData.Mapped(); uuidSchemeInitDataTuples = new ArrayList<UuidSchemeInitDataTuple>();
} }
drmInitData.put(contentProtection.uuid, contentProtection.data); uuidSchemeInitDataTuples.add(
new UuidSchemeInitDataTuple(contentProtection.uuid, contentProtection.data));
} }
} }
return drmInitData; return uuidSchemeInitDataTuples == null ? null
: new DrmInitData.Mapped(uuidSchemeInitDataTuples);
} }
private static long getPeriodDurationUs(MediaPresentationDescription manifest, int index) { private static long getPeriodDurationUs(MediaPresentationDescription manifest, int index) {
......
...@@ -16,16 +16,14 @@ ...@@ -16,16 +16,14 @@
package com.google.android.exoplayer.drm; package com.google.android.exoplayer.drm;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util;
import android.media.MediaDrm;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
/** /**
* Encapsulates initialization data required by a {@link MediaDrm} instances. * Encapsulates DRM initialization data for possibly multiple DRM schemes.
*/ */
public interface DrmInitData { public interface DrmInitData {
...@@ -42,31 +40,54 @@ public interface DrmInitData { ...@@ -42,31 +40,54 @@ public interface DrmInitData {
*/ */
final class Mapped implements DrmInitData { final class Mapped implements DrmInitData {
private final Map<UUID, SchemeInitData> schemeData; private final UuidSchemeInitDataTuple[] schemeDatas;
// Lazily initialized hashcode.
private int hashCode;
public Mapped(UuidSchemeInitDataTuple... schemeDatas) {
this.schemeDatas = schemeDatas.clone();
Arrays.sort(this.schemeDatas); // Required for correct equals and hashcode implementations.
}
public Mapped() { public Mapped(List<UuidSchemeInitDataTuple> schemeDatas) {
schemeData = new HashMap<>(); this.schemeDatas = schemeDatas.toArray(new UuidSchemeInitDataTuple[schemeDatas.size()]);
Arrays.sort(this.schemeDatas); // Required for correct equals and hashcode implementations.
} }
@Override @Override
public SchemeInitData get(UUID schemeUuid) { public SchemeInitData get(UUID schemeUuid) {
return schemeData.get(schemeUuid); for (UuidSchemeInitDataTuple schemeData : schemeDatas) {
if (schemeUuid.equals(schemeData.uuid)) {
return schemeData.data;
}
}
return null;
} }
/** @Override
* Inserts scheme specific initialization data. public int hashCode() {
* if (hashCode == 0) {
* @param schemeUuid The scheme UUID. hashCode = Arrays.hashCode(schemeDatas);
* @param schemeInitData The corresponding initialization data. }
*/ return hashCode;
public void put(UUID schemeUuid, SchemeInitData schemeInitData) { }
schemeData.put(schemeUuid, schemeInitData);
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return Arrays.equals(schemeDatas, ((Mapped) obj).schemeDatas);
} }
} }
/** /**
* A {@link DrmInitData} implementation that returns the same initialization data for all schemes. * A {@link DrmInitData} implementation that returns the same data for all schemes.
*/ */
final class Universal implements DrmInitData { final class Universal implements DrmInitData {
...@@ -81,6 +102,22 @@ public interface DrmInitData { ...@@ -81,6 +102,22 @@ public interface DrmInitData {
return data; return data;
} }
@Override
public int hashCode() {
return data == null ? 0 : data.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return Util.areEqual(data, ((Universal) obj).data);
}
} }
/** /**
...@@ -88,6 +125,9 @@ public interface DrmInitData { ...@@ -88,6 +125,9 @@ public interface DrmInitData {
*/ */
final class SchemeInitData { final class SchemeInitData {
// Lazily initialized hashcode.
private int hashCode;
/** /**
* The mimeType of {@link #data}. * The mimeType of {@link #data}.
*/ */
...@@ -114,14 +154,55 @@ public interface DrmInitData { ...@@ -114,14 +154,55 @@ public interface DrmInitData {
if (obj == this) { if (obj == this) {
return true; return true;
} }
SchemeInitData other = (SchemeInitData) obj; SchemeInitData other = (SchemeInitData) obj;
return mimeType.equals(other.mimeType) && Arrays.equals(data, other.data); return mimeType.equals(other.mimeType) && Arrays.equals(data, other.data);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return mimeType.hashCode() + 31 * Arrays.hashCode(data); if (hashCode == 0) {
hashCode = mimeType.hashCode() + 31 * Arrays.hashCode(data);
}
return hashCode;
}
}
/**
* A tuple consisting of a {@link UUID} and a {@link SchemeInitData}.
* <p>
* Implements {@link Comparable} based on {@link UUID} ordering.
*/
final class UuidSchemeInitDataTuple implements Comparable<UuidSchemeInitDataTuple> {
public final UUID uuid;
public final SchemeInitData data;
public UuidSchemeInitDataTuple(UUID uuid, SchemeInitData data) {
this.uuid = Assertions.checkNotNull(uuid);
this.data = Assertions.checkNotNull(data);
}
@Override
public int compareTo(UuidSchemeInitDataTuple another) {
return uuid.compareTo(another.uuid);
}
@Override
public int hashCode() {
return uuid.hashCode() + 31 * data.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
UuidSchemeInitDataTuple other = (UuidSchemeInitDataTuple) obj;
return Util.areEqual(uuid, other.uuid) && Util.areEqual(data, other.data);
} }
} }
......
...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.C; ...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData; import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
import com.google.android.exoplayer.extractor.ChunkIndex; import com.google.android.exoplayer.extractor.ChunkIndex;
import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput; import com.google.android.exoplayer.extractor.ExtractorInput;
...@@ -39,6 +40,7 @@ import android.util.Pair; ...@@ -39,6 +40,7 @@ import android.util.Pair;
import android.util.SparseArray; import android.util.SparseArray;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
...@@ -318,25 +320,25 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -318,25 +320,25 @@ public final class FragmentedMp4Extractor implements Extractor {
List<Atom.LeafAtom> moovLeafChildren = moov.leafChildren; List<Atom.LeafAtom> moovLeafChildren = moov.leafChildren;
int moovLeafChildrenSize = moovLeafChildren.size(); int moovLeafChildrenSize = moovLeafChildren.size();
DrmInitData.Mapped drmInitData = null; ArrayList<UuidSchemeInitDataTuple> uuidSchemeInitDataTuples = null;
for (int i = 0; i < moovLeafChildrenSize; i++) { for (int i = 0; i < moovLeafChildrenSize; i++) {
LeafAtom child = moovLeafChildren.get(i); LeafAtom child = moovLeafChildren.get(i);
if (child.type == Atom.TYPE_pssh) { if (child.type == Atom.TYPE_pssh) {
if (drmInitData == null) { if (uuidSchemeInitDataTuples == null) {
drmInitData = new DrmInitData.Mapped(); uuidSchemeInitDataTuples = new ArrayList<UuidSchemeInitDataTuple>();
} }
byte[] psshData = child.data.data; byte[] psshData = child.data.data;
UUID uuid = PsshAtomUtil.parseUuid(psshData); UUID uuid = PsshAtomUtil.parseUuid(psshData);
if (uuid == null) { if (uuid == null) {
Log.w(TAG, "Skipped pssh atom (failed to extract uuid)"); Log.w(TAG, "Skipped pssh atom (failed to extract uuid)");
} else { } else {
drmInitData.put(PsshAtomUtil.parseUuid(psshData), uuidSchemeInitDataTuples.add(new UuidSchemeInitDataTuple(uuid,
new SchemeInitData(MimeTypes.VIDEO_MP4, psshData)); new SchemeInitData(MimeTypes.VIDEO_MP4, psshData)));
} }
} }
} }
if (drmInitData != null) { if (uuidSchemeInitDataTuples != null) {
extractorOutput.drmInitData(drmInitData); extractorOutput.drmInitData(new DrmInitData.Mapped(uuidSchemeInitDataTuples));
} }
// Read declaration of track fragments in the Moov box. // Read declaration of track fragments in the Moov box.
......
...@@ -30,6 +30,7 @@ import com.google.android.exoplayer.chunk.FormatEvaluator; ...@@ -30,6 +30,7 @@ import com.google.android.exoplayer.chunk.FormatEvaluator;
import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData; import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
import com.google.android.exoplayer.extractor.mp4.TrackEncryptionBox; import com.google.android.exoplayer.extractor.mp4.TrackEncryptionBox;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.ProtectionElement; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.ProtectionElement;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement;
...@@ -119,9 +120,8 @@ public final class SmoothStreamingSampleSource implements SampleSource { ...@@ -119,9 +120,8 @@ public final class SmoothStreamingSampleSource implements SampleSource {
byte[] keyId = getProtectionElementKeyId(protectionElement.data); byte[] keyId = getProtectionElementKeyId(protectionElement.data);
trackEncryptionBoxes = new TrackEncryptionBox[1]; trackEncryptionBoxes = new TrackEncryptionBox[1];
trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId); trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId);
drmInitData = new DrmInitData.Mapped(); drmInitData = new DrmInitData.Mapped(new UuidSchemeInitDataTuple(protectionElement.uuid,
drmInitData.put(protectionElement.uuid, new SchemeInitData(MimeTypes.VIDEO_MP4, protectionElement.data)));
new SchemeInitData(MimeTypes.VIDEO_MP4, protectionElement.data));
} }
return true; return true;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment