mirror of
https://github.com/samsonjs/media.git
synced 2026-04-11 12:15:47 +00:00
Add SsManifest.copy and TrackKey for SmoothStreaming downloads
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=165295985
This commit is contained in:
parent
c94bce17b4
commit
4b706d9484
2 changed files with 205 additions and 9 deletions
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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.smoothstreaming.manifest;
|
||||
|
||||
import android.test.MoreAsserts;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.ProtectionElement;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SsManifest}.
|
||||
*/
|
||||
public class SsManifestTest extends TestCase {
|
||||
|
||||
private static ProtectionElement DUMMY_PROTECTION_ELEMENT =
|
||||
new ProtectionElement(C.WIDEVINE_UUID, new byte[] {0, 1, 2});
|
||||
|
||||
public void testCopy() throws Exception {
|
||||
Format[][] formats = newFormats(2, 3);
|
||||
SsManifest sourceManifest = newSsManifest(
|
||||
newStreamElement("1",formats[0]),
|
||||
newStreamElement("2", formats[1]));
|
||||
|
||||
List<TrackKey> keys = Arrays.asList(
|
||||
new TrackKey(0, 0),
|
||||
new TrackKey(0, 2),
|
||||
new TrackKey(1, 0));
|
||||
// Keys don't need to be in any particular order
|
||||
Collections.shuffle(keys, new Random(0));
|
||||
|
||||
SsManifest copyManifest = sourceManifest.copy(keys);
|
||||
|
||||
SsManifest expectedManifest = newSsManifest(
|
||||
newStreamElement("1", formats[0][0], formats[0][2]),
|
||||
newStreamElement("2", formats[1][0]));
|
||||
assertManifestEquals(expectedManifest, copyManifest);
|
||||
}
|
||||
|
||||
public void testCopyRemoveStreamElement() throws Exception {
|
||||
Format[][] formats = newFormats(2, 3);
|
||||
SsManifest sourceManifest = newSsManifest(
|
||||
newStreamElement("1", formats[0]),
|
||||
newStreamElement("2", formats[1]));
|
||||
|
||||
List<TrackKey> keys = Arrays.asList(
|
||||
new TrackKey(1, 0));
|
||||
// Keys don't need to be in any particular order
|
||||
Collections.shuffle(keys, new Random(0));
|
||||
|
||||
SsManifest copyManifest = sourceManifest.copy(keys);
|
||||
|
||||
SsManifest expectedManifest = newSsManifest(
|
||||
newStreamElement("2", formats[1][0]));
|
||||
assertManifestEquals(expectedManifest, copyManifest);
|
||||
}
|
||||
|
||||
private static void assertManifestEquals(SsManifest expected, SsManifest actual) {
|
||||
assertEquals(expected.durationUs, actual.durationUs);
|
||||
assertEquals(expected.dvrWindowLengthUs, actual.dvrWindowLengthUs);
|
||||
assertEquals(expected.isLive, actual.isLive);
|
||||
assertEquals(expected.lookAheadCount, actual.lookAheadCount);
|
||||
assertEquals(expected.majorVersion, actual.majorVersion);
|
||||
assertEquals(expected.minorVersion, actual.minorVersion);
|
||||
assertEquals(expected.protectionElement.uuid, actual.protectionElement.uuid);
|
||||
assertEquals(expected.protectionElement, actual.protectionElement);
|
||||
for (int i = 0; i < expected.streamElements.length; i++) {
|
||||
StreamElement expectedStreamElement = expected.streamElements[i];
|
||||
StreamElement actualStreamElement = actual.streamElements[i];
|
||||
assertEquals(expectedStreamElement.chunkCount, actualStreamElement.chunkCount);
|
||||
assertEquals(expectedStreamElement.displayHeight, actualStreamElement.displayHeight);
|
||||
assertEquals(expectedStreamElement.displayWidth, actualStreamElement.displayWidth);
|
||||
assertEquals(expectedStreamElement.language, actualStreamElement.language);
|
||||
assertEquals(expectedStreamElement.maxHeight, actualStreamElement.maxHeight);
|
||||
assertEquals(expectedStreamElement.maxWidth, actualStreamElement.maxWidth);
|
||||
assertEquals(expectedStreamElement.name, actualStreamElement.name);
|
||||
assertEquals(expectedStreamElement.subType, actualStreamElement.subType);
|
||||
assertEquals(expectedStreamElement.timescale, actualStreamElement.timescale);
|
||||
assertEquals(expectedStreamElement.type, actualStreamElement.type);
|
||||
MoreAsserts.assertEquals(expectedStreamElement.formats, actualStreamElement.formats);
|
||||
}
|
||||
}
|
||||
|
||||
private static Format[][] newFormats(int streamElementCount, int trackCounts) {
|
||||
Format[][] formats = new Format[streamElementCount][];
|
||||
for (int i = 0; i < streamElementCount; i++) {
|
||||
formats[i] = new Format[trackCounts];
|
||||
for (int j = 0; j < trackCounts; j++) {
|
||||
formats[i][j] = newFormat(i + "." + j);
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
|
||||
private static SsManifest newSsManifest(StreamElement... streamElements) {
|
||||
return new SsManifest(1, 2, 1000, 5000, 0, 0, false, DUMMY_PROTECTION_ELEMENT, streamElements);
|
||||
}
|
||||
|
||||
private static StreamElement newStreamElement(String name, Format... formats) {
|
||||
return new StreamElement("baseUri", "chunkTemplate", C.TRACK_TYPE_VIDEO, "subType",
|
||||
1000, name, 1024, 768, 1024, 768, null, formats, Collections.<Long>emptyList(), 0);
|
||||
}
|
||||
|
||||
private static Format newFormat(String id) {
|
||||
return Format.createContainerFormat(id, MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null,
|
||||
Format.NO_VALUE, 0, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,6 +21,9 @@ import com.google.android.exoplayer2.Format;
|
|||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.UriUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -96,16 +99,60 @@ public class SsManifest {
|
|||
public SsManifest(int majorVersion, int minorVersion, long timescale, long duration,
|
||||
long dvrWindowLength, int lookAheadCount, boolean isLive, ProtectionElement protectionElement,
|
||||
StreamElement[] streamElements) {
|
||||
this(majorVersion, minorVersion,
|
||||
duration == 0 ? C.TIME_UNSET
|
||||
: Util.scaleLargeTimestamp(duration, C.MICROS_PER_SECOND, timescale),
|
||||
dvrWindowLength == 0 ? C.TIME_UNSET
|
||||
: Util.scaleLargeTimestamp(dvrWindowLength, C.MICROS_PER_SECOND, timescale),
|
||||
lookAheadCount, isLive, protectionElement, streamElements);
|
||||
}
|
||||
|
||||
private SsManifest(int majorVersion, int minorVersion, long durationUs, long dvrWindowLengthUs,
|
||||
int lookAheadCount, boolean isLive, ProtectionElement protectionElement,
|
||||
StreamElement[] streamElements) {
|
||||
this.majorVersion = majorVersion;
|
||||
this.minorVersion = minorVersion;
|
||||
this.durationUs = durationUs;
|
||||
this.dvrWindowLengthUs = dvrWindowLengthUs;
|
||||
this.lookAheadCount = lookAheadCount;
|
||||
this.isLive = isLive;
|
||||
this.protectionElement = protectionElement;
|
||||
this.streamElements = streamElements;
|
||||
dvrWindowLengthUs = dvrWindowLength == 0 ? C.TIME_UNSET
|
||||
: Util.scaleLargeTimestamp(dvrWindowLength, C.MICROS_PER_SECOND, timescale);
|
||||
durationUs = duration == 0 ? C.TIME_UNSET
|
||||
: Util.scaleLargeTimestamp(duration, C.MICROS_PER_SECOND, timescale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this manifest which includes only the tracks identified by the given keys.
|
||||
*
|
||||
* @param trackKeys List of keys for the tracks to be included in the copy.
|
||||
* @return A copy of this manifest with the selected tracks.
|
||||
* @throws IndexOutOfBoundsException If a key has an invalid index.
|
||||
*/
|
||||
public final SsManifest copy(List<TrackKey> trackKeys) {
|
||||
LinkedList<TrackKey> sortedKeys = new LinkedList<>(trackKeys);
|
||||
Collections.sort(sortedKeys);
|
||||
|
||||
StreamElement currentStreamElement = null;
|
||||
List<StreamElement> copiedStreamElements = new ArrayList<>();
|
||||
List<Format> copiedFormats = new ArrayList<>();
|
||||
for (int i = 0; i < sortedKeys.size(); i++) {
|
||||
TrackKey key = sortedKeys.get(i);
|
||||
StreamElement streamElement = streamElements[key.streamElementIndex];
|
||||
if (streamElement != currentStreamElement && currentStreamElement != null) {
|
||||
// We're advancing to a new stream element. Add the current one.
|
||||
copiedStreamElements.add(currentStreamElement.copy(copiedFormats.toArray(new Format[0])));
|
||||
copiedFormats.clear();
|
||||
}
|
||||
currentStreamElement = streamElement;
|
||||
copiedFormats.add(streamElement.formats[key.trackIndex]);
|
||||
}
|
||||
if (currentStreamElement != null) {
|
||||
// Add the last stream element.
|
||||
copiedStreamElements.add(currentStreamElement.copy(copiedFormats.toArray(new Format[0])));
|
||||
}
|
||||
|
||||
StreamElement[] copiedStreamElementsArray = copiedStreamElements.toArray(new StreamElement[0]);
|
||||
return new SsManifest(majorVersion, minorVersion, durationUs, dvrWindowLengthUs, lookAheadCount,
|
||||
isLive, protectionElement, copiedStreamElementsArray);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -156,6 +203,16 @@ public class SsManifest {
|
|||
long timescale, String name, int maxWidth, int maxHeight, int displayWidth,
|
||||
int displayHeight, String language, Format[] formats, List<Long> chunkStartTimes,
|
||||
long lastChunkDuration) {
|
||||
this (baseUri, chunkTemplate, type, subType, timescale, name, maxWidth, maxHeight,
|
||||
displayWidth, displayHeight, language, formats, chunkStartTimes,
|
||||
Util.scaleLargeTimestamps(chunkStartTimes, C.MICROS_PER_SECOND, timescale),
|
||||
Util.scaleLargeTimestamp(lastChunkDuration, C.MICROS_PER_SECOND, timescale));
|
||||
}
|
||||
|
||||
private StreamElement(String baseUri, String chunkTemplate, int type, String subType,
|
||||
long timescale, String name, int maxWidth, int maxHeight, int displayWidth,
|
||||
int displayHeight, String language, Format[] formats, List<Long> chunkStartTimes,
|
||||
long[] chunkStartTimesUs, long lastChunkDurationUs) {
|
||||
this.baseUri = baseUri;
|
||||
this.chunkTemplate = chunkTemplate;
|
||||
this.type = type;
|
||||
|
|
@ -168,12 +225,23 @@ public class SsManifest {
|
|||
this.displayHeight = displayHeight;
|
||||
this.language = language;
|
||||
this.formats = formats;
|
||||
this.chunkCount = chunkStartTimes.size();
|
||||
this.chunkStartTimes = chunkStartTimes;
|
||||
lastChunkDurationUs =
|
||||
Util.scaleLargeTimestamp(lastChunkDuration, C.MICROS_PER_SECOND, timescale);
|
||||
chunkStartTimesUs =
|
||||
Util.scaleLargeTimestamps(chunkStartTimes, C.MICROS_PER_SECOND, timescale);
|
||||
this.chunkStartTimesUs = chunkStartTimesUs;
|
||||
this.lastChunkDurationUs = lastChunkDurationUs;
|
||||
chunkCount = chunkStartTimes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this stream element with the formats replaced with those specified.
|
||||
*
|
||||
* @param formats The formats to be included in the copy.
|
||||
* @return A copy of this stream element with the formats replaced.
|
||||
* @throws IndexOutOfBoundsException If a key has an invalid index.
|
||||
*/
|
||||
public StreamElement copy(Format[] formats) {
|
||||
return new StreamElement(baseUri, chunkTemplate, type, subType, timescale, name, maxWidth,
|
||||
maxHeight, displayWidth, displayHeight, language, formats, chunkStartTimes,
|
||||
chunkStartTimesUs, lastChunkDurationUs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue