mirror of
https://github.com/samsonjs/media.git
synced 2026-03-30 10:15:48 +00:00
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:
parent
a0c1595725
commit
4a5bb55920
3 changed files with 305 additions and 215 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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>> {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue