diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifest.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifest.java index 095ad1a3ee..51284f06c4 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifest.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifest.java @@ -20,6 +20,9 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.offline.FilterableManifest; import com.google.android.exoplayer2.offline.StreamKey; +import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator; +import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; +import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.UriUtil; import com.google.android.exoplayer2.util.Util; @@ -36,123 +39,7 @@ import java.util.UUID; */ public class SsManifest implements FilterableManifest { - public static final int UNSET_LOOKAHEAD = -1; - - /** - * The client manifest major version. - */ - public final int majorVersion; - - /** - * The client manifest minor version. - */ - public final int minorVersion; - - /** - * The number of fragments in a lookahead, or {@link #UNSET_LOOKAHEAD} if the lookahead is - * unspecified. - */ - public final int lookAheadCount; - - /** - * Whether the manifest describes a live presentation still in progress. - */ - public final boolean isLive; - - /** - * Content protection information, or null if the content is not protected. - */ - public final ProtectionElement protectionElement; - - /** - * The contained stream elements. - */ - public final StreamElement[] streamElements; - - /** - * The overall presentation duration of the media in microseconds, or {@link C#TIME_UNSET} - * if the duration is unknown. - */ - public final long durationUs; - - /** - * The length of the trailing window for a live broadcast in microseconds, or - * {@link C#TIME_UNSET} if the stream is not live or if the window length is unspecified. - */ - public final long dvrWindowLengthUs; - - /** - * @param majorVersion The client manifest major version. - * @param minorVersion The client manifest minor version. - * @param timescale The timescale of the media as the number of units that pass in one second. - * @param duration The overall presentation duration in units of the timescale attribute, or 0 - * if the duration is unknown. - * @param dvrWindowLength The length of the trailing window in units of the timescale attribute, - * or 0 if this attribute is unspecified or not applicable. - * @param lookAheadCount The number of fragments in a lookahead, or {@link #UNSET_LOOKAHEAD} if - * this attribute is unspecified or not applicable. - * @param isLive True if the manifest describes a live presentation still in progress. False - * otherwise. - * @param protectionElement Content protection information, or null if the content is not - * protected. - * @param streamElements The contained stream elements. - */ - 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; - } - - @Override - public final SsManifest copy(List streamKeys) { - ArrayList sortedKeys = new ArrayList<>(streamKeys); - Collections.sort(sortedKeys); - - StreamElement currentStreamElement = null; - List copiedStreamElements = new ArrayList<>(); - List copiedFormats = new ArrayList<>(); - for (int i = 0; i < sortedKeys.size(); i++) { - StreamKey key = sortedKeys.get(i); - StreamElement streamElement = streamElements[key.groupIndex]; - 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); - } - - /** - * Represents a protection element containing a single header. - */ + /** Represents a protection element containing a single header. */ public static class ProtectionElement { public final UUID uuid; @@ -162,7 +49,45 @@ public class SsManifest implements FilterableManifest { this.uuid = uuid; this.data = data; } + } + /** {@link MediaChunkIterator} wrapping a track of a {@link StreamElement}. */ + public static final class StreamElementIterator extends BaseMediaChunkIterator { + + private final StreamElement streamElement; + private final int trackIndex; + + /** + * Creates iterator. + * + * @param streamElement The {@link StreamElement} to wrap. + * @param trackIndex The track index in the stream element. + * @param chunkIndex The chunk index at which the iterator will start. + */ + public StreamElementIterator(StreamElement streamElement, int trackIndex, int chunkIndex) { + super(/* fromIndex= */ chunkIndex, /* toIndex= */ streamElement.chunkCount - 1); + this.streamElement = streamElement; + this.trackIndex = trackIndex; + } + + @Override + public DataSpec getDataSpec() { + checkInBounds(); + Uri uri = streamElement.buildRequestUri(trackIndex, (int) getCurrentIndex()); + return new DataSpec(uri); + } + + @Override + public long getChunkStartTimeUs() { + checkInBounds(); + return streamElement.getStartTimeUs((int) getCurrentIndex()); + } + + @Override + public long getChunkEndTimeUs() { + long chunkStartTimeUs = getChunkStartTimeUs(); + return chunkStartTimeUs + streamElement.getChunkDurationUs((int) getCurrentIndex()); + } } /** @@ -302,7 +227,136 @@ public class SsManifest implements FilterableManifest { .replace(URL_PLACEHOLDER_START_TIME_2, startTimeString); return UriUtil.resolveToUri(baseUri, chunkUrl); } - } + public static final int UNSET_LOOKAHEAD = -1; + + /** The client manifest major version. */ + public final int majorVersion; + + /** The client manifest minor version. */ + public final int minorVersion; + + /** + * The number of fragments in a lookahead, or {@link #UNSET_LOOKAHEAD} if the lookahead is + * unspecified. + */ + public final int lookAheadCount; + + /** Whether the manifest describes a live presentation still in progress. */ + public final boolean isLive; + + /** Content protection information, or null if the content is not protected. */ + public final ProtectionElement protectionElement; + + /** The contained stream elements. */ + public final StreamElement[] streamElements; + + /** + * The overall presentation duration of the media in microseconds, or {@link C#TIME_UNSET} if the + * duration is unknown. + */ + public final long durationUs; + + /** + * The length of the trailing window for a live broadcast in microseconds, or {@link C#TIME_UNSET} + * if the stream is not live or if the window length is unspecified. + */ + public final long dvrWindowLengthUs; + + /** + * @param majorVersion The client manifest major version. + * @param minorVersion The client manifest minor version. + * @param timescale The timescale of the media as the number of units that pass in one second. + * @param duration The overall presentation duration in units of the timescale attribute, or 0 if + * the duration is unknown. + * @param dvrWindowLength The length of the trailing window in units of the timescale attribute, + * or 0 if this attribute is unspecified or not applicable. + * @param lookAheadCount The number of fragments in a lookahead, or {@link #UNSET_LOOKAHEAD} if + * this attribute is unspecified or not applicable. + * @param isLive True if the manifest describes a live presentation still in progress. False + * otherwise. + * @param protectionElement Content protection information, or null if the content is not + * protected. + * @param streamElements The contained stream elements. + */ + 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; + } + + @Override + public final SsManifest copy(List streamKeys) { + ArrayList sortedKeys = new ArrayList<>(streamKeys); + Collections.sort(sortedKeys); + + StreamElement currentStreamElement = null; + List copiedStreamElements = new ArrayList<>(); + List copiedFormats = new ArrayList<>(); + for (int i = 0; i < sortedKeys.size(); i++) { + StreamKey key = sortedKeys.get(i); + StreamElement streamElement = streamElements[key.groupIndex]; + 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); + } }