Split DashMediaSource MediaPeriod implementation into DashMediaPeriod and support multiple periods.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=127315877
This commit is contained in:
eguven 2016-07-13 07:40:56 -07:00 committed by Oliver Woodman
parent a0c1595725
commit 4a5bb55920
3 changed files with 305 additions and 215 deletions

View file

@ -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<Representation> representations = adaptationSet.representations;
long periodDurationUs = getPeriodDurationUs(index);
List<Representation> 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<Representation> representations = manifest.getPeriod(0).adaptationSets
.get(adaptationSetIndex).representations;
long periodDurationUs = getPeriodDurationUs(index);
List<Representation> 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<Representation> 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;

View file

@ -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<ChunkSampleStream<DashChunkSource>> {
private final Loader loader;
private final DataSourceFactory dataSourceFactory;
private final BandwidthMeter bandwidthMeter;
private final EventDispatcher eventDispatcher;
private ChunkSampleStream<DashChunkSource>[] 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<DashChunkSource> 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<SampleStream> oldStreams,
List<TrackSelection> newSelections, long positionUs) {
int newEnabledSourceCount = sampleStreams.length + newSelections.size() - oldStreams.size();
ChunkSampleStream<DashChunkSource>[] newSampleStreams =
newSampleStreamArray(newEnabledSourceCount);
int newEnabledSourceIndex = 0;
// Iterate over currently enabled streams, either releasing them or adding them to the new
// list.
for (ChunkSampleStream<DashChunkSource> 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<DashChunkSource> 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<DashChunkSource> sampleStream : sampleStreams) {
sampleStream.seekToUs(positionUs);
}
return positionUs;
}
@Override
public void release() {
if (sampleStreams != null) {
for (ChunkSampleStream<DashChunkSource> 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<DashChunkSource> 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<Representation> 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<DashChunkSource> 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<DashChunkSource>[] newSampleStreamArray(int length) {
return new ChunkSampleStream[length];
}
}

View file

@ -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<ChunkSampleStream<DashChunkSource>> {
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<DashChunkSource>[] 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<SampleStream> oldStreams,
List<TrackSelection> newSelections, long positionUs) {
int newEnabledSourceCount = sampleStreams.length + newSelections.size() - oldStreams.size();
ChunkSampleStream<DashChunkSource>[] newSampleStreams =
newSampleStreamArray(newEnabledSourceCount);
int newEnabledSourceIndex = 0;
// Iterate over currently enabled streams, either releasing them or adding them to the new list.
for (ChunkSampleStream<DashChunkSource> 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<DashChunkSource> 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<DashChunkSource> 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<DashChunkSource> 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<DashChunkSource> 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<DashChunkSource> 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<Representation> 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<DashChunkSource> 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<DashChunkSource>[] newSampleStreamArray(int length) {
return new ChunkSampleStream[length];
}
private final class ManifestCallback implements
Loader.Callback<ParsingLoadable<MediaPresentationDescription>> {