diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashChunkSource.java index b35fe8c064..b19802d569 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashChunkSource.java @@ -34,9 +34,7 @@ import com.google.android.exoplayer2.source.chunk.FormatEvaluator.Evaluation; import com.google.android.exoplayer2.source.chunk.InitializationChunk; import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.SingleSampleMediaChunk; -import com.google.android.exoplayer2.source.dash.mpd.AdaptationSet; import com.google.android.exoplayer2.source.dash.mpd.MediaPresentationDescription; -import com.google.android.exoplayer2.source.dash.mpd.Period; import com.google.android.exoplayer2.source.dash.mpd.RangedUri; import com.google.android.exoplayer2.source.dash.mpd.Representation; import com.google.android.exoplayer2.upstream.DataSource; @@ -84,11 +82,11 @@ public class DashChunkSource implements ChunkSource { * @param adaptiveFormatEvaluator For adaptive tracks, selects from the available formats. * @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between * server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified - * as the server's unix time minus the local elapsed time. It unknown, set to 0. + * as the server's unix time minus the local elapsed time. If unknown, set to 0. */ public DashChunkSource(Loader manifestLoader, MediaPresentationDescription manifest, int adaptationSetIndex, TrackGroup trackGroup, int[] tracks, DataSource dataSource, - FormatEvaluator adaptiveFormatEvaluator, long elapsedRealtimeOffsetMs) { + FormatEvaluator adaptiveFormatEvaluator, long elapsedRealtimeOffsetMs, int index) { this.manifestLoader = manifestLoader; this.manifest = manifest; this.adaptationSetIndex = adaptationSetIndex; @@ -98,12 +96,10 @@ public class DashChunkSource implements ChunkSource { this.elapsedRealtimeOffsetUs = elapsedRealtimeOffsetMs * 1000; this.evaluation = new Evaluation(); - Period period = manifest.getPeriod(0); - long periodDurationUs = getPeriodDurationUs(manifest, 0); - AdaptationSet adaptationSet = period.adaptationSets.get(adaptationSetIndex); - - List representations = adaptationSet.representations; + long periodDurationUs = getPeriodDurationUs(index); + List representations = getRepresentations(index); representationHolders = new RepresentationHolder[representations.size()]; + for (int i = 0; i < representations.size(); i++) { Representation representation = representations.get(i); representationHolders[i] = new RepresentationHolder(periodDurationUs, representation); @@ -121,12 +117,11 @@ public class DashChunkSource implements ChunkSource { } } - public void updateManifest(MediaPresentationDescription newManifest) { + public void updateManifest(MediaPresentationDescription newManifest, int index) { try { manifest = newManifest; - long periodDurationUs = getPeriodDurationUs(manifest, 0); - List representations = manifest.getPeriod(0).adaptationSets - .get(adaptationSetIndex).representations; + long periodDurationUs = getPeriodDurationUs(index); + List representations = getRepresentations(index); for (int i = 0; i < representationHolders.length; i++) { Representation representation = representations.get(i); representationHolders[i].updateRepresentation(periodDurationUs, representation); @@ -136,6 +131,10 @@ public class DashChunkSource implements ChunkSource { } } + private List getRepresentations(int index) { + return manifest.getPeriod(index).adaptationSets.get(adaptationSetIndex).representations; + } + // ChunkSource implementation. @Override @@ -356,7 +355,7 @@ public class DashChunkSource implements ChunkSource { throw new IllegalStateException("Invalid format: " + format); } - private static long getPeriodDurationUs(MediaPresentationDescription manifest, int index) { + private long getPeriodDurationUs(int index) { long durationMs = manifest.getPeriodDuration(index); if (durationMs == -1) { return C.UNSET_TIME_US; diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java new file mode 100644 index 0000000000..83c8573421 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2016 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; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.CompositeSequenceableLoader; +import com.google.android.exoplayer2.source.MediaPeriod; +import com.google.android.exoplayer2.source.SampleStream; +import com.google.android.exoplayer2.source.SequenceableLoader; +import com.google.android.exoplayer2.source.TrackGroup; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.source.chunk.ChunkSampleStream; +import com.google.android.exoplayer2.source.chunk.FormatEvaluator; +import com.google.android.exoplayer2.source.dash.mpd.AdaptationSet; +import com.google.android.exoplayer2.source.dash.mpd.MediaPresentationDescription; +import com.google.android.exoplayer2.source.dash.mpd.Period; +import com.google.android.exoplayer2.source.dash.mpd.Representation; +import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.upstream.Allocator; +import com.google.android.exoplayer2.upstream.BandwidthMeter; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSourceFactory; +import com.google.android.exoplayer2.upstream.Loader; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** + * A DASH {@link MediaPeriod}. + */ +/* package */ final class DashMediaPeriod implements MediaPeriod, + SequenceableLoader.Callback> { + + private final Loader loader; + private final DataSourceFactory dataSourceFactory; + private final BandwidthMeter bandwidthMeter; + private final EventDispatcher eventDispatcher; + + private ChunkSampleStream[] sampleStreams; + private CompositeSequenceableLoader sequenceableLoader; + private Callback callback; + private Allocator allocator; + private long durationUs; + private TrackGroupArray trackGroups; + private int[] trackGroupAdaptationSetIndices; + private MediaPresentationDescription manifest; + private int index; + private int minLoadableRetryCount; + private long elapsedRealtimeOffset; + private Period period; + + public DashMediaPeriod(MediaPresentationDescription manifest, int index, + DataSourceFactory dataSourceFactory, BandwidthMeter bandwidthMeter, + int minLoadableRetryCount, EventDispatcher eventDispatcher, long elapsedRealtimeOffset, + Loader loader) { + this.manifest = manifest; + this.index = index; + this.dataSourceFactory = dataSourceFactory; + this.bandwidthMeter = bandwidthMeter; + this.minLoadableRetryCount = minLoadableRetryCount; + this.eventDispatcher = eventDispatcher; + this.elapsedRealtimeOffset = elapsedRealtimeOffset; + this.loader = loader; + period = manifest.getPeriod(index); + durationUs = manifest.dynamic ? C.UNSET_TIME_US : manifest.getPeriodDuration(index) * 1000; + } + + public void updateManifest(MediaPresentationDescription manifest, int index) { + this.manifest = manifest; + this.index = index; + period = manifest.getPeriod(index); + if (sampleStreams != null) { + for (ChunkSampleStream sampleStream : sampleStreams) { + sampleStream.getChunkSource().updateManifest(manifest, index); + } + callback.onContinueLoadingRequested(this); + } + } + + // MediaPeriod implementation. + + @Override + public void prepare(Callback callback, Allocator allocator, long positionUs) { + this.callback = callback; + this.allocator = allocator; + sampleStreams = newSampleStreamArray(0); + sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); + buildTrackGroups(); + callback.onPeriodPrepared(this); + } + + @Override + public void maybeThrowPrepareError() throws IOException { + loader.maybeThrowError(); + } + + @Override + public long getDurationUs() { + return durationUs; + } + + @Override + public TrackGroupArray getTrackGroups() { + return trackGroups; + } + + @Override + public SampleStream[] selectTracks(List oldStreams, + List newSelections, long positionUs) { + int newEnabledSourceCount = sampleStreams.length + newSelections.size() - oldStreams.size(); + ChunkSampleStream[] newSampleStreams = + newSampleStreamArray(newEnabledSourceCount); + int newEnabledSourceIndex = 0; + + // Iterate over currently enabled streams, either releasing them or adding them to the new + // list. + for (ChunkSampleStream sampleStream : sampleStreams) { + if (oldStreams.contains(sampleStream)) { + sampleStream.release(); + } else { + newSampleStreams[newEnabledSourceIndex++] = sampleStream; + } + } + + // Instantiate and return new streams. + SampleStream[] streamsToReturn = new SampleStream[newSelections.size()]; + for (int i = 0; i < newSelections.size(); i++) { + newSampleStreams[newEnabledSourceIndex] = + buildSampleStream(newSelections.get(i), positionUs); + streamsToReturn[i] = newSampleStreams[newEnabledSourceIndex]; + newEnabledSourceIndex++; + } + + sampleStreams = newSampleStreams; + sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); + return streamsToReturn; + } + + @Override + public boolean continueLoading(long positionUs) { + return sequenceableLoader.continueLoading(positionUs); + } + + @Override + public long getNextLoadPositionUs() { + return sequenceableLoader.getNextLoadPositionUs(); + } + + @Override + public long readDiscontinuity() { + return C.UNSET_TIME_US; + } + + @Override + public long getBufferedPositionUs() { + long bufferedPositionUs = Long.MAX_VALUE; + for (ChunkSampleStream sampleStream : sampleStreams) { + long rendererBufferedPositionUs = sampleStream.getBufferedPositionUs(); + if (rendererBufferedPositionUs != C.END_OF_SOURCE_US) { + bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs); + } + } + return bufferedPositionUs == Long.MAX_VALUE ? C.END_OF_SOURCE_US : bufferedPositionUs; + } + + @Override + public long seekToUs(long positionUs) { + for (ChunkSampleStream sampleStream : sampleStreams) { + sampleStream.seekToUs(positionUs); + } + return positionUs; + } + + @Override + public void release() { + if (sampleStreams != null) { + for (ChunkSampleStream sampleStream : sampleStreams) { + sampleStream.release(); + } + sampleStreams = null; + } + sequenceableLoader = null; + callback = null; + allocator = null; + durationUs = 0; + trackGroups = null; + trackGroupAdaptationSetIndices = null; + } + + // SequenceableLoader.Callback implementation. + + @Override + public void onContinueLoadingRequested(ChunkSampleStream sampleStream) { + callback.onContinueLoadingRequested(this); + } + + // Internal methods. + + private void buildTrackGroups() { + int trackGroupCount = 0; + trackGroupAdaptationSetIndices = new int[period.adaptationSets.size()]; + TrackGroup[] trackGroupArray = new TrackGroup[period.adaptationSets.size()]; + for (int i = 0; i < period.adaptationSets.size(); i++) { + AdaptationSet adaptationSet = period.adaptationSets.get(i); + int adaptationSetType = adaptationSet.type; + List representations = adaptationSet.representations; + if (!representations.isEmpty() && (adaptationSetType == C.TRACK_TYPE_AUDIO + || adaptationSetType == C.TRACK_TYPE_VIDEO || adaptationSetType == C.TRACK_TYPE_TEXT)) { + Format[] formats = new Format[representations.size()]; + for (int j = 0; j < formats.length; j++) { + formats[j] = representations.get(j).format; + } + trackGroupAdaptationSetIndices[trackGroupCount] = i; + boolean adaptive = adaptationSetType == C.TRACK_TYPE_VIDEO; + trackGroupArray[trackGroupCount++] = new TrackGroup(adaptive, formats); + } + } + if (trackGroupCount < trackGroupArray.length) { + trackGroupAdaptationSetIndices = Arrays.copyOf(trackGroupAdaptationSetIndices, + trackGroupCount); + trackGroupArray = Arrays.copyOf(trackGroupArray, trackGroupCount); + } + trackGroups = new TrackGroupArray(trackGroupArray); + } + + private ChunkSampleStream buildSampleStream(TrackSelection selection, + long positionUs) { + int[] selectedTracks = selection.getTracks(); + FormatEvaluator adaptiveEvaluator = selectedTracks.length > 1 + ? new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter) : null; + int adaptationSetIndex = trackGroupAdaptationSetIndices[selection.group]; + AdaptationSet adaptationSet = period.adaptationSets.get( + adaptationSetIndex); + int adaptationSetType = adaptationSet.type; + DataSource dataSource = + dataSourceFactory.createDataSource(bandwidthMeter); + DashChunkSource chunkSource = new DashChunkSource(loader, manifest, adaptationSetIndex, + trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator, + elapsedRealtimeOffset, index); + return new ChunkSampleStream<>(adaptationSetType, chunkSource, this, allocator, positionUs, + minLoadableRetryCount, eventDispatcher); + } + + @SuppressWarnings("unchecked") + private static ChunkSampleStream[] newSampleStreamArray(int length) { + return new ChunkSampleStream[length]; + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index 4a6dc4ee75..ff582313e1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -16,34 +16,19 @@ package com.google.android.exoplayer2.source.dash; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; -import com.google.android.exoplayer2.source.CompositeSequenceableLoader; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.SampleStream; -import com.google.android.exoplayer2.source.SequenceableLoader; -import com.google.android.exoplayer2.source.TrackGroup; -import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.source.chunk.ChunkSampleStream; -import com.google.android.exoplayer2.source.chunk.FormatEvaluator; -import com.google.android.exoplayer2.source.chunk.FormatEvaluator.AdaptiveEvaluator; -import com.google.android.exoplayer2.source.dash.mpd.AdaptationSet; import com.google.android.exoplayer2.source.dash.mpd.MediaPresentationDescription; import com.google.android.exoplayer2.source.dash.mpd.MediaPresentationDescriptionParser; -import com.google.android.exoplayer2.source.dash.mpd.Period; -import com.google.android.exoplayer2.source.dash.mpd.Representation; import com.google.android.exoplayer2.source.dash.mpd.UtcTimingElement; -import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSourceFactory; import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.ParsingLoadable; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import android.net.Uri; @@ -57,16 +42,13 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.List; import java.util.Locale; import java.util.TimeZone; /** * A DASH {@link MediaSource}. */ -public final class DashMediaSource implements MediaPeriod, MediaSource, - SequenceableLoader.Callback> { +public final class DashMediaSource implements MediaSource { /** * The default minimum number of times to retry loading data prior to failing. @@ -84,22 +66,14 @@ public final class DashMediaSource implements MediaPeriod, MediaSource, private DataSource dataSource; private Loader loader; - private ChunkSampleStream[] sampleStreams; - private CompositeSequenceableLoader sequenceableLoader; private Uri manifestUri; private long manifestLoadStartTimestamp; private long manifestLoadEndTimestamp; private MediaPresentationDescription manifest; - - private Callback callback; - private Allocator allocator; private Handler manifestRefreshHandler; - private boolean prepared; - private long durationUs; + private DashMediaPeriod[] periods; private long elapsedRealtimeOffset; - private TrackGroupArray trackGroups; - private int[] trackGroupAdaptationSetIndices; public DashMediaSource(Uri manifestUri, DataSourceFactory dataSourceFactory, BandwidthMeter bandwidthMeter, Handler eventHandler, @@ -118,29 +92,14 @@ public final class DashMediaSource implements MediaPeriod, MediaSource, eventDispatcher = new EventDispatcher(eventHandler, eventListener); manifestParser = new MediaPresentationDescriptionParser(); manifestCallback = new ManifestCallback(); + // TODO: Remove this call when prepare() is a part of MediaSource. + prepare(dataSourceFactory); } // MediaSource implementation. - @Override - public int getPeriodCount() { - return 1; - } - - @Override - public MediaPeriod createPeriod(int index) { - Assertions.checkArgument(index == 0); - return this; - } - - // MediaPeriod implementation. - - @Override - public void prepare(Callback callback, Allocator allocator, long positionUs) { - this.callback = callback; - this.allocator = allocator; - sampleStreams = newSampleStreamArray(0); - sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); + // TODO @Override + public void prepare(DataSourceFactory dataSourceFactory) { dataSource = dataSourceFactory.createDataSource(); loader = new Loader("Loader:DashMediaSource"); manifestRefreshHandler = new Handler(); @@ -148,120 +107,36 @@ public final class DashMediaSource implements MediaPeriod, MediaSource, } @Override - public void maybeThrowPrepareError() throws IOException { - loader.maybeThrowError(); - } - - @Override - public long getDurationUs() { - return durationUs; - } - - @Override - public TrackGroupArray getTrackGroups() { - return trackGroups; - } - - @Override - public SampleStream[] selectTracks(List oldStreams, - List newSelections, long positionUs) { - int newEnabledSourceCount = sampleStreams.length + newSelections.size() - oldStreams.size(); - ChunkSampleStream[] newSampleStreams = - newSampleStreamArray(newEnabledSourceCount); - int newEnabledSourceIndex = 0; - - // Iterate over currently enabled streams, either releasing them or adding them to the new list. - for (ChunkSampleStream sampleStream : sampleStreams) { - if (oldStreams.contains(sampleStream)) { - sampleStream.release(); - } else { - newSampleStreams[newEnabledSourceIndex++] = sampleStream; - } + public int getPeriodCount() { + if (manifest == null) { + return UNKNOWN_PERIOD_COUNT; } + return manifest.getPeriodCount(); + } - // Instantiate and return new streams. - SampleStream[] streamsToReturn = new SampleStream[newSelections.size()]; - for (int i = 0; i < newSelections.size(); i++) { - newSampleStreams[newEnabledSourceIndex] = buildSampleStream(newSelections.get(i), positionUs); - streamsToReturn[i] = newSampleStreams[newEnabledSourceIndex]; - newEnabledSourceIndex++; + @Override + public MediaPeriod createPeriod(int index) { + if (periods == null) { + return null; } - - sampleStreams = newSampleStreams; - sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); - return streamsToReturn; + return periods[index]; } - @Override - public boolean continueLoading(long positionUs) { - return sequenceableLoader.continueLoading(positionUs); - } - - @Override - public long getNextLoadPositionUs() { - return sequenceableLoader.getNextLoadPositionUs(); - } - - @Override - public long readDiscontinuity() { - return C.UNSET_TIME_US; - } - - @Override - public long getBufferedPositionUs() { - long bufferedPositionUs = Long.MAX_VALUE; - for (ChunkSampleStream sampleStream : sampleStreams) { - long rendererBufferedPositionUs = sampleStream.getBufferedPositionUs(); - if (rendererBufferedPositionUs != C.END_OF_SOURCE_US) { - bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs); - } - } - return bufferedPositionUs == Long.MAX_VALUE ? C.END_OF_SOURCE_US : bufferedPositionUs; - } - - @Override - public long seekToUs(long positionUs) { - for (ChunkSampleStream sampleStream : sampleStreams) { - sampleStream.seekToUs(positionUs); - } - return positionUs; - } - - @Override + // TODO @Override public void release() { dataSource = null; if (loader != null) { loader.release(); loader = null; } - if (sampleStreams != null) { - for (ChunkSampleStream sampleStream : sampleStreams) { - sampleStream.release(); - } - sampleStreams = null; - } - sequenceableLoader = null; manifestLoadStartTimestamp = 0; manifestLoadEndTimestamp = 0; manifest = null; - callback = null; - allocator = null; if (manifestRefreshHandler != null) { manifestRefreshHandler.removeCallbacksAndMessages(null); manifestRefreshHandler = null; } - prepared = false; - durationUs = 0; elapsedRealtimeOffset = 0; - trackGroups = null; - trackGroupAdaptationSetIndices = null; - } - - // SequenceableLoader.Callback implementation. - - @Override - public void onContinueLoadingRequested(ChunkSampleStream sampleStream) { - callback.onContinueLoadingRequested(this); } // Loadable callbacks. @@ -276,20 +151,17 @@ public final class DashMediaSource implements MediaPeriod, MediaSource, if (manifest.location != null) { manifestUri = manifest.location; } - if (!prepared) { - durationUs = manifest.dynamic ? C.UNSET_TIME_US : manifest.getPeriodDuration(0) * 1000; - buildTrackGroups(manifest); + + if (periods == null) { if (manifest.utcTiming != null) { resolveUtcTimingElement(manifest.utcTiming); } else { finishPrepare(); } } else { - for (ChunkSampleStream sampleStream : sampleStreams) { - sampleStream.getChunkSource().updateManifest(manifest); + for (int i = 0; i < periods.length; i++) { + periods[i].updateManifest(manifest, i); } - callback.onContinueLoadingRequested(this); - scheduleManifestRefresh(); } } @@ -371,8 +243,12 @@ public final class DashMediaSource implements MediaPeriod, MediaSource, } private void finishPrepare() { - prepared = true; - callback.onPeriodPrepared(this); + int periodCount = manifest.getPeriodCount(); + periods = new DashMediaPeriod[periodCount]; + for (int i = 0; i < periodCount; i++) { + periods[i] = new DashMediaPeriod(manifest, i, dataSourceFactory, bandwidthMeter, + minLoadableRetryCount, eventDispatcher, elapsedRealtimeOffset, loader); + } scheduleManifestRefresh(); } @@ -404,56 +280,6 @@ public final class DashMediaSource implements MediaPeriod, MediaSource, eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, elapsedRealtimeMs); } - private void buildTrackGroups(MediaPresentationDescription manifest) { - Period period = manifest.getPeriod(0); - int trackGroupCount = 0; - trackGroupAdaptationSetIndices = new int[period.adaptationSets.size()]; - TrackGroup[] trackGroupArray = new TrackGroup[period.adaptationSets.size()]; - for (int i = 0; i < period.adaptationSets.size(); i++) { - AdaptationSet adaptationSet = period.adaptationSets.get(i); - int adaptationSetType = adaptationSet.type; - List representations = adaptationSet.representations; - if (!representations.isEmpty() && (adaptationSetType == C.TRACK_TYPE_AUDIO - || adaptationSetType == C.TRACK_TYPE_VIDEO || adaptationSetType == C.TRACK_TYPE_TEXT)) { - Format[] formats = new Format[representations.size()]; - for (int j = 0; j < formats.length; j++) { - formats[j] = representations.get(j).format; - } - trackGroupAdaptationSetIndices[trackGroupCount] = i; - boolean adaptive = adaptationSetType == C.TRACK_TYPE_VIDEO; - trackGroupArray[trackGroupCount++] = new TrackGroup(adaptive, formats); - } - } - if (trackGroupCount < trackGroupArray.length) { - trackGroupAdaptationSetIndices = Arrays.copyOf(trackGroupAdaptationSetIndices, - trackGroupCount); - trackGroupArray = Arrays.copyOf(trackGroupArray, trackGroupCount); - } - trackGroups = new TrackGroupArray(trackGroupArray); - } - - private ChunkSampleStream buildSampleStream(TrackSelection selection, - long positionUs) { - int[] selectedTracks = selection.getTracks(); - FormatEvaluator adaptiveEvaluator = selectedTracks.length > 1 - ? new AdaptiveEvaluator(bandwidthMeter) : null; - int adaptationSetIndex = trackGroupAdaptationSetIndices[selection.group]; - AdaptationSet adaptationSet = manifest.getPeriod(0).adaptationSets.get( - adaptationSetIndex); - int adaptationSetType = adaptationSet.type; - DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter); - DashChunkSource chunkSource = new DashChunkSource(loader, manifest, adaptationSetIndex, - trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator, - elapsedRealtimeOffset); - return new ChunkSampleStream<>(adaptationSetType, chunkSource, this, allocator, positionUs, - minLoadableRetryCount, eventDispatcher); - } - - @SuppressWarnings("unchecked") - private static ChunkSampleStream[] newSampleStreamArray(int length) { - return new ChunkSampleStream[length]; - } - private final class ManifestCallback implements Loader.Callback> {