Add RepresentationKey and DashManifest copy method
RepresentationKey defines a representation location in a DashManifest. DashManifest copy method creates a copy of the manifest which includes only the representations pointed by the given RepresentationKeys. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=150195990
This commit is contained in:
parent
f092c4446f
commit
76c9968211
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.source.dash.manifest;
|
||||
|
||||
import android.net.Uri;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DashManifest}.
|
||||
*/
|
||||
public class DashManifestTest extends TestCase {
|
||||
|
||||
private static final UtcTimingElement DUMMY_UTC_TIMING = new UtcTimingElement("", "");
|
||||
private static final List<SchemeValuePair> DUMMY_ACCESSIBILITY_DESCRIPTORS =
|
||||
Collections.emptyList();
|
||||
private static final SingleSegmentBase DUMMY_SEGMENT_BASE = new SingleSegmentBase();
|
||||
private static final Format DUMMY_FORMAT = Format.createSampleFormat("", "", 0);
|
||||
|
||||
public void testCopy() throws Exception {
|
||||
Representation[][][] representations = newRepresentations(3, 2, 3);
|
||||
DashManifest sourceManifest = newDashManifest(10,
|
||||
newPeriod("1", 1,
|
||||
newAdaptationSet(2, representations[0][0]),
|
||||
newAdaptationSet(3, representations[0][1])),
|
||||
newPeriod("4", 4,
|
||||
newAdaptationSet(5, representations[1][0]),
|
||||
newAdaptationSet(6, representations[1][1])),
|
||||
newPeriod("7", 7,
|
||||
newAdaptationSet(8, representations[2][0]),
|
||||
newAdaptationSet(9, representations[2][1])));
|
||||
|
||||
List<RepresentationKey> keys = Arrays.asList(
|
||||
new RepresentationKey(0, 0, 0),
|
||||
new RepresentationKey(0, 0, 1),
|
||||
new RepresentationKey(0, 1, 2),
|
||||
|
||||
new RepresentationKey(1, 0, 1),
|
||||
new RepresentationKey(1, 1, 0),
|
||||
new RepresentationKey(1, 1, 2),
|
||||
|
||||
new RepresentationKey(2, 0, 1),
|
||||
new RepresentationKey(2, 0, 2),
|
||||
new RepresentationKey(2, 1, 0));
|
||||
// Keys don't need to be in any particular order
|
||||
Collections.shuffle(keys, new Random(0));
|
||||
|
||||
DashManifest copyManifest = sourceManifest.copy(keys);
|
||||
|
||||
DashManifest expectedManifest = newDashManifest(10,
|
||||
newPeriod("1", 1,
|
||||
newAdaptationSet(2, representations[0][0][0], representations[0][0][1]),
|
||||
newAdaptationSet(3, representations[0][1][2])),
|
||||
newPeriod("4", 4,
|
||||
newAdaptationSet(5, representations[1][0][1]),
|
||||
newAdaptationSet(6, representations[1][1][0], representations[1][1][2])),
|
||||
newPeriod("7", 7,
|
||||
newAdaptationSet(8, representations[2][0][1], representations[2][0][2]),
|
||||
newAdaptationSet(9, representations[2][1][0])));
|
||||
assertManifestEquals(expectedManifest, copyManifest);
|
||||
}
|
||||
|
||||
public void testCopySameAdaptationIndexButDifferentPeriod() throws Exception {
|
||||
Representation[][][] representations = newRepresentations(2, 1, 1);
|
||||
DashManifest sourceManifest = newDashManifest(10,
|
||||
newPeriod("1", 1,
|
||||
newAdaptationSet(2, representations[0][0])),
|
||||
newPeriod("4", 4,
|
||||
newAdaptationSet(5, representations[1][0])));
|
||||
|
||||
DashManifest copyManifest = sourceManifest.copy(Arrays.asList(
|
||||
new RepresentationKey(0, 0, 0),
|
||||
new RepresentationKey(1, 0, 0)));
|
||||
|
||||
DashManifest expectedManifest = newDashManifest(10,
|
||||
newPeriod("1", 1,
|
||||
newAdaptationSet(2, representations[0][0])),
|
||||
newPeriod("4", 4,
|
||||
newAdaptationSet(5, representations[1][0])));
|
||||
assertManifestEquals(expectedManifest, copyManifest);
|
||||
}
|
||||
|
||||
public void testCopySkipPeriod() throws Exception {
|
||||
Representation[][][] representations = newRepresentations(3, 2, 3);
|
||||
DashManifest sourceManifest = newDashManifest(10,
|
||||
newPeriod("1", 1,
|
||||
newAdaptationSet(2, representations[0][0]),
|
||||
newAdaptationSet(3, representations[0][1])),
|
||||
newPeriod("4", 4,
|
||||
newAdaptationSet(5, representations[1][0]),
|
||||
newAdaptationSet(6, representations[1][1])),
|
||||
newPeriod("7", 7,
|
||||
newAdaptationSet(8, representations[2][0]),
|
||||
newAdaptationSet(9, representations[2][1])));
|
||||
|
||||
DashManifest copyManifest = sourceManifest.copy(Arrays.asList(
|
||||
new RepresentationKey(0, 0, 0),
|
||||
new RepresentationKey(0, 0, 1),
|
||||
new RepresentationKey(0, 1, 2),
|
||||
|
||||
new RepresentationKey(2, 0, 1),
|
||||
new RepresentationKey(2, 0, 2),
|
||||
new RepresentationKey(2, 1, 0)));
|
||||
|
||||
DashManifest expectedManifest = newDashManifest(7,
|
||||
newPeriod("1", 1,
|
||||
newAdaptationSet(2, representations[0][0][0], representations[0][0][1]),
|
||||
newAdaptationSet(3, representations[0][1][2])),
|
||||
newPeriod("7", 4,
|
||||
newAdaptationSet(8, representations[2][0][1], representations[2][0][2]),
|
||||
newAdaptationSet(9, representations[2][1][0])));
|
||||
assertManifestEquals(expectedManifest, copyManifest);
|
||||
}
|
||||
|
||||
private static void assertManifestEquals(DashManifest expected, DashManifest actual) {
|
||||
assertEquals(expected.availabilityStartTime, actual.availabilityStartTime);
|
||||
assertEquals(expected.duration, actual.duration);
|
||||
assertEquals(expected.minBufferTime, actual.minBufferTime);
|
||||
assertEquals(expected.dynamic, actual.dynamic);
|
||||
assertEquals(expected.minUpdatePeriod, actual.minUpdatePeriod);
|
||||
assertEquals(expected.timeShiftBufferDepth, actual.timeShiftBufferDepth);
|
||||
assertEquals(expected.suggestedPresentationDelay, actual.suggestedPresentationDelay);
|
||||
assertEquals(expected.utcTiming, actual.utcTiming);
|
||||
assertEquals(expected.location, actual.location);
|
||||
assertEquals(expected.getPeriodCount(), actual.getPeriodCount());
|
||||
for (int i = 0; i < expected.getPeriodCount(); i++) {
|
||||
Period expectedPeriod = expected.getPeriod(i);
|
||||
Period actualPeriod = actual.getPeriod(i);
|
||||
assertEquals(expectedPeriod.id, actualPeriod.id);
|
||||
assertEquals(expectedPeriod.startMs, actualPeriod.startMs);
|
||||
List<AdaptationSet> expectedAdaptationSets = expectedPeriod.adaptationSets;
|
||||
List<AdaptationSet> actualAdaptationSets = actualPeriod.adaptationSets;
|
||||
assertEquals(expectedAdaptationSets.size(), actualAdaptationSets.size());
|
||||
for (int j = 0; j < expectedAdaptationSets.size(); j++) {
|
||||
AdaptationSet expectedAdaptationSet = expectedAdaptationSets.get(j);
|
||||
AdaptationSet actualAdaptationSet = actualAdaptationSets.get(j);
|
||||
assertEquals(expectedAdaptationSet.id, actualAdaptationSet.id);
|
||||
assertEquals(expectedAdaptationSet.type, actualAdaptationSet.type);
|
||||
assertEquals(expectedAdaptationSet.accessibilityDescriptors,
|
||||
actualAdaptationSet.accessibilityDescriptors);
|
||||
assertEquals(expectedAdaptationSet.representations, actualAdaptationSet.representations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Representation[][][] newRepresentations(int periodCount, int adaptationSetCounts,
|
||||
int representationCounts) {
|
||||
Representation[][][] representations = new Representation[periodCount][][];
|
||||
for (int i = 0; i < periodCount; i++) {
|
||||
representations[i] = new Representation[adaptationSetCounts][];
|
||||
for (int j = 0; j < adaptationSetCounts; j++) {
|
||||
representations[i][j] = new Representation[representationCounts];
|
||||
for (int k = 0; k < representationCounts; k++) {
|
||||
representations[i][j][k] = newRepresentation();
|
||||
}
|
||||
}
|
||||
}
|
||||
return representations;
|
||||
}
|
||||
|
||||
private static Representation newRepresentation() {
|
||||
return Representation.newInstance("", 0, DUMMY_FORMAT, "", DUMMY_SEGMENT_BASE);
|
||||
}
|
||||
|
||||
private static DashManifest newDashManifest(int duration, Period... periods) {
|
||||
return new DashManifest(0, duration, 1, false, 2, 3, 4, DUMMY_UTC_TIMING, Uri.EMPTY,
|
||||
Arrays.asList(periods));
|
||||
}
|
||||
|
||||
private static Period newPeriod(String id, int startMs, AdaptationSet... adaptationSets) {
|
||||
return new Period(id, startMs, Arrays.asList(adaptationSets));
|
||||
}
|
||||
|
||||
private static AdaptationSet newAdaptationSet(int seed, Representation... representations) {
|
||||
return new AdaptationSet(++seed, ++seed, Arrays.asList(representations),
|
||||
DUMMY_ACCESSIBILITY_DESCRIPTORS);
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,9 @@ package com.google.android.exoplayer2.source.dash.manifest;
|
||||
|
||||
import android.net.Uri;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -79,4 +81,64 @@ public class DashManifest {
|
||||
return C.msToUs(getPeriodDurationMs(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this manifest which includes only the representations identified by the given
|
||||
* keys.
|
||||
*
|
||||
* @param representationKeys List of keys for the representations to be included in the copy.
|
||||
* @return A copy of this manifest with the selected representations.
|
||||
* @throws IndexOutOfBoundsException If a key has an invalid index.
|
||||
*/
|
||||
public final DashManifest copy(List<RepresentationKey> representationKeys) {
|
||||
LinkedList<RepresentationKey> keys = new LinkedList<>(representationKeys);
|
||||
Collections.sort(keys);
|
||||
keys.add(new RepresentationKey(-1, -1, -1)); // Add a stopper key to the end
|
||||
|
||||
ArrayList<Period> copyPeriods = new ArrayList<>();
|
||||
long shiftMs = 0;
|
||||
for (int periodIndex = 0; periodIndex < getPeriodCount(); periodIndex++) {
|
||||
if (keys.peek().periodIndex != periodIndex) {
|
||||
// No representations selected in this period.
|
||||
long periodDurationMs = getPeriodDurationMs(periodIndex);
|
||||
if (periodDurationMs != C.TIME_UNSET) {
|
||||
shiftMs += periodDurationMs;
|
||||
}
|
||||
} else {
|
||||
Period period = getPeriod(periodIndex);
|
||||
ArrayList<AdaptationSet> copyAdaptationSets =
|
||||
copyAdaptationSets(period.adaptationSets, keys);
|
||||
copyPeriods.add(new Period(period.id, period.startMs - shiftMs, copyAdaptationSets));
|
||||
}
|
||||
}
|
||||
long newDuration = duration != C.TIME_UNSET ? duration - shiftMs : C.TIME_UNSET;
|
||||
return new DashManifest(availabilityStartTime, newDuration, minBufferTime, dynamic,
|
||||
minUpdatePeriod, timeShiftBufferDepth, suggestedPresentationDelay, utcTiming, location,
|
||||
copyPeriods);
|
||||
}
|
||||
|
||||
private static ArrayList<AdaptationSet> copyAdaptationSets(
|
||||
List<AdaptationSet> adaptationSets, LinkedList<RepresentationKey> keys) {
|
||||
RepresentationKey key = keys.poll();
|
||||
int periodIndex = key.periodIndex;
|
||||
ArrayList<AdaptationSet> copyAdaptationSets = new ArrayList<>();
|
||||
do {
|
||||
int adaptationSetIndex = key.adaptationSetIndex;
|
||||
AdaptationSet adaptationSet = adaptationSets.get(adaptationSetIndex);
|
||||
|
||||
List<Representation> representations = adaptationSet.representations;
|
||||
ArrayList<Representation> copyRepresentations = new ArrayList<>();
|
||||
do {
|
||||
Representation representation = representations.get(key.representationIndex);
|
||||
copyRepresentations.add(representation);
|
||||
key = keys.poll();
|
||||
} while(key.periodIndex == periodIndex && key.adaptationSetIndex == adaptationSetIndex);
|
||||
|
||||
copyAdaptationSets.add(new AdaptationSet(adaptationSet.id, adaptationSet.type,
|
||||
copyRepresentations, adaptationSet.accessibilityDescriptors));
|
||||
} while(key.periodIndex == periodIndex);
|
||||
// Add back the last key which doesn't belong to the period being processed
|
||||
keys.addFirst(key);
|
||||
return copyAdaptationSets;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.source.dash.manifest;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Uniquely identifies a {@link Representation} in a {@link DashManifest}.
|
||||
*/
|
||||
public final class RepresentationKey implements Parcelable, Comparable<RepresentationKey> {
|
||||
|
||||
public final int periodIndex;
|
||||
public final int adaptationSetIndex;
|
||||
public final int representationIndex;
|
||||
|
||||
public RepresentationKey(int periodIndex, int adaptationSetIndex, int representationIndex) {
|
||||
this.periodIndex = periodIndex;
|
||||
this.adaptationSetIndex = adaptationSetIndex;
|
||||
this.representationIndex = representationIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return periodIndex + "." + adaptationSetIndex + "." + representationIndex;
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(periodIndex);
|
||||
dest.writeInt(adaptationSetIndex);
|
||||
dest.writeInt(representationIndex);
|
||||
}
|
||||
|
||||
public static final Creator<RepresentationKey> CREATOR =
|
||||
new Creator<RepresentationKey>() {
|
||||
@Override
|
||||
public RepresentationKey createFromParcel(Parcel in) {
|
||||
return new RepresentationKey(in.readInt(), in.readInt(), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepresentationKey[] newArray(int size) {
|
||||
return new RepresentationKey[size];
|
||||
}
|
||||
};
|
||||
|
||||
// Comparable implementation.
|
||||
|
||||
@Override
|
||||
public int compareTo(RepresentationKey o) {
|
||||
int result = periodIndex - o.periodIndex;
|
||||
if (result == 0) {
|
||||
result = adaptationSetIndex - o.adaptationSetIndex;
|
||||
if (result == 0) {
|
||||
result = representationIndex - o.representationIndex;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user