Commit b120bea0 by andrewlewis Committed by Oliver Woodman

Don't reuse MediaPeriods.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=130266483
parent b5e41a90
...@@ -229,7 +229,7 @@ import java.io.IOException; ...@@ -229,7 +229,7 @@ import java.io.IOException;
// MediaPeriod.Callback implementation. // MediaPeriod.Callback implementation.
@Override @Override
public void onPeriodPrepared(MediaPeriod source) { public void onPrepared(MediaPeriod source) {
handler.obtainMessage(MSG_PERIOD_PREPARED, source).sendToTarget(); handler.obtainMessage(MSG_PERIOD_PREPARED, source).sendToTarget();
} }
...@@ -906,6 +906,7 @@ import java.io.IOException; ...@@ -906,6 +906,7 @@ import java.io.IOException;
public void updatePeriods() throws ExoPlaybackException, IOException { public void updatePeriods() throws ExoPlaybackException, IOException {
if (timeline == null) { if (timeline == null) {
// We're waiting to get information about periods. // We're waiting to get information about periods.
mediaSource.maybeThrowSourceInfoRefreshError();
return; return;
} }
...@@ -927,11 +928,14 @@ import java.io.IOException; ...@@ -927,11 +928,14 @@ import java.io.IOException;
} }
} }
MediaPeriod mediaPeriod; if (periodIndex >= timeline.getPeriodCount()) {
if (startPositionUs != C.UNSET_TIME_US // This period is not available yet.
&& (mediaPeriod = mediaSource.createPeriod(periodIndex)) != null) { mediaSource.maybeThrowSourceInfoRefreshError();
Period newPeriod = new Period(renderers, rendererCapabilities, trackSelector, mediaPeriod, } else if (startPositionUs != C.UNSET_TIME_US) {
timeline.getPeriodId(periodIndex), periodIndex, startPositionUs); MediaPeriod mediaPeriod = mediaSource.createPeriod(periodIndex, this,
loadControl.getAllocator(), startPositionUs);
Period newPeriod = new Period(renderers, rendererCapabilities, trackSelector, mediaSource,
mediaPeriod, timeline.getPeriodId(periodIndex), periodIndex, startPositionUs);
newPeriod.isLast = timeline.isFinal() && periodIndex == timeline.getPeriodCount() - 1; newPeriod.isLast = timeline.isFinal() && periodIndex == timeline.getPeriodCount() - 1;
if (loadingPeriod != null) { if (loadingPeriod != null) {
loadingPeriod.setNextPeriod(newPeriod); loadingPeriod.setNextPeriod(newPeriod);
...@@ -941,7 +945,6 @@ import java.io.IOException; ...@@ -941,7 +945,6 @@ import java.io.IOException;
bufferAheadPeriodCount++; bufferAheadPeriodCount++;
loadingPeriod = newPeriod; loadingPeriod = newPeriod;
setIsLoading(true); setIsLoading(true);
loadingPeriod.mediaPeriod.preparePeriod(this, loadControl.getAllocator(), startPositionUs);
} }
} }
...@@ -1169,17 +1172,19 @@ import java.io.IOException; ...@@ -1169,17 +1172,19 @@ import java.io.IOException;
private final Renderer[] renderers; private final Renderer[] renderers;
private final RendererCapabilities[] rendererCapabilities; private final RendererCapabilities[] rendererCapabilities;
private final TrackSelector trackSelector; private final TrackSelector trackSelector;
private final MediaSource mediaSource;
private Object trackSelectionData; private Object trackSelectionData;
private TrackSelectionArray trackSelections; private TrackSelectionArray trackSelections;
private TrackSelectionArray periodTrackSelections; private TrackSelectionArray periodTrackSelections;
public Period(Renderer[] renderers, RendererCapabilities[] rendererCapabilities, public Period(Renderer[] renderers, RendererCapabilities[] rendererCapabilities,
TrackSelector trackSelector, MediaPeriod mediaPeriod, Object id, int index, TrackSelector trackSelector, MediaSource mediaSource, MediaPeriod mediaPeriod, Object id,
long positionUs) { int index, long positionUs) {
this.renderers = renderers; this.renderers = renderers;
this.rendererCapabilities = rendererCapabilities; this.rendererCapabilities = rendererCapabilities;
this.trackSelector = trackSelector; this.trackSelector = trackSelector;
this.mediaSource = mediaSource;
this.mediaPeriod = mediaPeriod; this.mediaPeriod = mediaPeriod;
this.id = Assertions.checkNotNull(id); this.id = Assertions.checkNotNull(id);
sampleStreams = new SampleStream[renderers.length]; sampleStreams = new SampleStream[renderers.length];
...@@ -1250,7 +1255,7 @@ import java.io.IOException; ...@@ -1250,7 +1255,7 @@ import java.io.IOException;
public void release() { public void release() {
try { try {
mediaPeriod.releasePeriod(); mediaSource.releasePeriod(mediaPeriod);
} catch (RuntimeException e) { } catch (RuntimeException e) {
// There's nothing we can do. // There's nothing we can do.
Log.e(TAG, "Period release failed.", e); Log.e(TAG, "Period release failed.", e);
......
...@@ -16,9 +16,13 @@ ...@@ -16,9 +16,13 @@
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import android.util.Pair; import android.util.Pair;
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/** /**
* Concatenates multiple {@link MediaSource}s. * Concatenates multiple {@link MediaSource}s.
...@@ -28,6 +32,7 @@ public final class ConcatenatingMediaSource implements MediaSource { ...@@ -28,6 +32,7 @@ public final class ConcatenatingMediaSource implements MediaSource {
private final MediaSource[] mediaSources; private final MediaSource[] mediaSources;
private final Timeline[] timelines; private final Timeline[] timelines;
private final Object[] manifests; private final Object[] manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private ConcatenatedTimeline timeline; private ConcatenatedTimeline timeline;
...@@ -38,6 +43,7 @@ public final class ConcatenatingMediaSource implements MediaSource { ...@@ -38,6 +43,7 @@ public final class ConcatenatingMediaSource implements MediaSource {
this.mediaSources = mediaSources; this.mediaSources = mediaSources;
timelines = new Timeline[mediaSources.length]; timelines = new Timeline[mediaSources.length];
manifests = new Object[mediaSources.length]; manifests = new Object[mediaSources.length];
sourceIndexByMediaPeriod = new HashMap<>();
} }
@Override @Override
...@@ -85,10 +91,28 @@ public final class ConcatenatingMediaSource implements MediaSource { ...@@ -85,10 +91,28 @@ public final class ConcatenatingMediaSource implements MediaSource {
} }
@Override @Override
public MediaPeriod createPeriod(int index) throws IOException { public void maybeThrowSourceInfoRefreshError() throws IOException {
for (MediaSource mediaSource : mediaSources) {
mediaSource.maybeThrowSourceInfoRefreshError();
}
}
@Override
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
long positionUs) {
int sourceIndex = timeline.getSourceIndexForPeriod(index); int sourceIndex = timeline.getSourceIndexForPeriod(index);
int periodIndexInSource = index - timeline.getFirstPeriodIndexInSource(sourceIndex); int periodIndexInSource = index - timeline.getFirstPeriodIndexInSource(sourceIndex);
return mediaSources[sourceIndex].createPeriod(periodIndexInSource); MediaPeriod mediaPeriod = mediaSources[sourceIndex].createPeriod(periodIndexInSource, callback,
allocator, positionUs);
sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex);
return mediaPeriod;
}
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
int sourceIndex = sourceIndexByMediaPeriod.get(mediaPeriod);
sourceIndexByMediaPeriod.remove(mediaPeriod);
mediaSources[sourceIndex].releasePeriod(mediaPeriod);
} }
@Override @Override
......
...@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source; ...@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException; import java.io.IOException;
/** /**
...@@ -39,24 +38,11 @@ public interface MediaPeriod extends SequenceableLoader { ...@@ -39,24 +38,11 @@ public interface MediaPeriod extends SequenceableLoader {
* *
* @param mediaPeriod The prepared {@link MediaPeriod}. * @param mediaPeriod The prepared {@link MediaPeriod}.
*/ */
void onPeriodPrepared(MediaPeriod mediaPeriod); void onPrepared(MediaPeriod mediaPeriod);
} }
/** /**
* Starts preparation of the period.
* <p>
* {@link Callback#onPeriodPrepared(MediaPeriod)} is called when preparation completes. If
* preparation fails, {@link #maybeThrowPrepareError()} will throw an {@link IOException} if
* called.
*
* @param callback A callback to receive updates from the period.
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
* @param positionUs The player's current playback position.
*/
void preparePeriod(Callback callback, Allocator allocator, long positionUs);
/**
* Throws an error that's preventing the period from becoming prepared. Does nothing if no such * Throws an error that's preventing the period from becoming prepared. Does nothing if no such
* error exists. * error exists.
* <p> * <p>
...@@ -136,12 +122,4 @@ public interface MediaPeriod extends SequenceableLoader { ...@@ -136,12 +122,4 @@ public interface MediaPeriod extends SequenceableLoader {
*/ */
long seekToUs(long positionUs); long seekToUs(long positionUs);
/**
* Releases the period.
* <p>
* This method should be called when the period is no longer required. It may be called in any
* state.
*/
void releasePeriod();
} }
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException; import java.io.IOException;
/** /**
...@@ -105,16 +107,31 @@ public interface MediaSource { ...@@ -105,16 +107,31 @@ public interface MediaSource {
Position getDefaultStartPosition(int index); Position getDefaultStartPosition(int index);
/** /**
* Returns a {@link MediaPeriod} corresponding to the period at the specified index, or * Throws any pending error encountered while loading or refreshing source information.
* {@code null} if the period at the specified index is not yet available. */
void maybeThrowSourceInfoRefreshError() throws IOException;
/**
* Returns a {@link MediaPeriod} corresponding to the period at the specified index.
* <p>
* {@link Callback#onPrepared(MediaPeriod)} is called when the new period is prepared. If
* preparation fails, {@link MediaPeriod#maybeThrowPrepareError()} will throw an
* {@link IOException} if called on the returned instance.
* *
* @param index The index of the period. * @param index The index of the period.
* @return A {@link MediaPeriod}, or {@code null} if the source at the specified index is not * @param callback A callback to receive updates from the period.
* available. * @param allocator An {@link Allocator} from which to obtain media buffer allocations.
* @throws IOException If there is an error that's preventing the source from becoming prepared or * @param positionUs The player's current playback position.
* creating periods. * @return A new {@link MediaPeriod}.
*/
MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, long positionUs);
/**
* Releases the period.
*
* @param mediaPeriod The period to release.
*/ */
MediaPeriod createPeriod(int index) throws IOException; void releasePeriod(MediaPeriod mediaPeriod);
/** /**
* Releases the source. * Releases the source.
......
...@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source; ...@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
...@@ -25,30 +24,23 @@ import java.util.IdentityHashMap; ...@@ -25,30 +24,23 @@ import java.util.IdentityHashMap;
/** /**
* Merges multiple {@link MediaPeriod} instances. * Merges multiple {@link MediaPeriod} instances.
*/ */
public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callback { /* package */ final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callback {
private final MediaPeriod[] periods; public final MediaPeriod[] periods;
private final Callback callback;
private final IdentityHashMap<SampleStream, Integer> streamPeriodIndices; private final IdentityHashMap<SampleStream, Integer> streamPeriodIndices;
private Callback callback;
private int pendingChildPrepareCount; private int pendingChildPrepareCount;
private TrackGroupArray trackGroups; private TrackGroupArray trackGroups;
private MediaPeriod[] enabledPeriods; private MediaPeriod[] enabledPeriods;
private SequenceableLoader sequenceableLoader; private SequenceableLoader sequenceableLoader;
public MergingMediaPeriod(MediaPeriod... periods) { public MergingMediaPeriod(Callback callback, MediaPeriod... periods) {
this.periods = periods; this.periods = periods;
pendingChildPrepareCount = periods.length;
streamPeriodIndices = new IdentityHashMap<>();
}
@Override
public void preparePeriod(Callback callback, Allocator allocator, long positionUs) {
this.callback = callback; this.callback = callback;
for (MediaPeriod period : periods) { streamPeriodIndices = new IdentityHashMap<>();
period.preparePeriod(this, allocator, positionUs); pendingChildPrepareCount = periods.length;
}
} }
@Override @Override
...@@ -174,17 +166,10 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba ...@@ -174,17 +166,10 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba
return positionUs; return positionUs;
} }
@Override
public void releasePeriod() {
for (MediaPeriod period : periods) {
period.releasePeriod();
}
}
// MediaPeriod.Callback implementation // MediaPeriod.Callback implementation
@Override @Override
public void onPeriodPrepared(MediaPeriod ignored) { public void onPrepared(MediaPeriod ignored) {
if (--pendingChildPrepareCount > 0) { if (--pendingChildPrepareCount > 0) {
return; return;
} }
...@@ -202,7 +187,7 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba ...@@ -202,7 +187,7 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba
} }
} }
trackGroups = new TrackGroupArray(trackGroupArray); trackGroups = new TrackGroupArray(trackGroupArray);
callback.onPeriodPrepared(this); callback.onPrepared(this);
} }
@Override @Override
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException; import java.io.IOException;
...@@ -73,13 +75,31 @@ public final class MergingMediaSource implements MediaSource { ...@@ -73,13 +75,31 @@ public final class MergingMediaSource implements MediaSource {
} }
@Override @Override
public MediaPeriod createPeriod(int index) throws IOException { public void maybeThrowSourceInfoRefreshError() throws IOException {
for (MediaSource mediaSource : mediaSources) {
mediaSource.maybeThrowSourceInfoRefreshError();
}
}
@Override
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
long positionUs) {
MediaPeriod[] periods = new MediaPeriod[mediaSources.length]; MediaPeriod[] periods = new MediaPeriod[mediaSources.length];
// The periods are only referenced after they have all been prepared.
MergingMediaPeriod mergingPeriod = new MergingMediaPeriod(callback, periods);
for (int i = 0; i < periods.length; i++) { for (int i = 0; i < periods.length; i++) {
periods[i] = mediaSources[i].createPeriod(index); periods[i] = mediaSources[i].createPeriod(index, mergingPeriod, allocator, positionUs);
Assertions.checkState(periods[i] != null, "Child source must not return null period"); Assertions.checkState(periods[i] != null, "Child source must not return null period");
} }
return new MergingMediaPeriod(periods); return mergingPeriod;
}
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
MergingMediaPeriod mergingPeriod = (MergingMediaPeriod) mediaPeriod;
for (int i = 0; i < mediaSources.length; i++) {
mediaSources[i].releasePeriod(mergingPeriod.periods[i]);
}
} }
@Override @Override
......
/*
* 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;
import android.net.Uri;
import android.os.Handler;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.source.SingleSampleMediaSource.EventListener;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.Loadable;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* A {@link MediaPeriod} with a single sample.
*/
/* package */ final class SingleSampleMediaPeriod implements MediaPeriod,
Loader.Callback<SingleSampleMediaPeriod.SourceLoadable> {
/**
* The initial size of the allocation used to hold the sample data.
*/
private static final int INITIAL_SAMPLE_SIZE = 1;
private final Uri uri;
private final DataSource.Factory dataSourceFactory;
private final int minLoadableRetryCount;
private final Handler eventHandler;
private final EventListener eventListener;
private final int eventSourceId;
private final TrackGroupArray tracks;
private final ArrayList<SampleStreamImpl> sampleStreams;
/* package */ final Loader loader;
/* package */ final Format format;
/* package */ boolean loadingFinished;
/* package */ byte[] sampleData;
/* package */ int sampleSize;
public SingleSampleMediaPeriod(Uri uri, DataSource.Factory dataSourceFactory, Format format,
int minLoadableRetryCount, Handler eventHandler, EventListener eventListener,
int eventSourceId) {
this.uri = uri;
this.dataSourceFactory = dataSourceFactory;
this.format = format;
this.minLoadableRetryCount = minLoadableRetryCount;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
this.eventSourceId = eventSourceId;
tracks = new TrackGroupArray(new TrackGroup(format));
sampleStreams = new ArrayList<>();
loader = new Loader("Loader:SingleSampleMediaPeriod");
sampleData = new byte[INITIAL_SAMPLE_SIZE];
}
public void release() {
loader.release();
}
@Override
public void maybeThrowPrepareError() throws IOException {
loader.maybeThrowError();
}
@Override
public TrackGroupArray getTrackGroups() {
return tracks;
}
@Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
for (int i = 0; i < selections.length; i++) {
if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
sampleStreams.remove(streams[i]);
streams[i] = null;
}
if (streams[i] == null && selections[i] != null) {
SampleStreamImpl stream = new SampleStreamImpl();
sampleStreams.add(stream);
streams[i] = stream;
streamResetFlags[i] = true;
}
}
return positionUs;
}
@Override
public boolean continueLoading(long positionUs) {
if (loadingFinished || loader.isLoading()) {
return false;
}
loader.startLoading(new SourceLoadable(uri, dataSourceFactory.createDataSource()), this,
minLoadableRetryCount);
return true;
}
@Override
public long readDiscontinuity() {
return C.UNSET_TIME_US;
}
@Override
public long getNextLoadPositionUs() {
return loadingFinished || loader.isLoading() ? C.END_OF_SOURCE_US : 0;
}
@Override
public long getBufferedPositionUs() {
return loadingFinished ? C.END_OF_SOURCE_US : 0;
}
@Override
public long seekToUs(long positionUs) {
for (int i = 0; i < sampleStreams.size(); i++) {
sampleStreams.get(i).seekToUs(positionUs);
}
return positionUs;
}
// Loader.Callback implementation.
@Override
public void onLoadCompleted(SourceLoadable loadable, long elapsedRealtimeMs,
long loadDurationMs) {
sampleSize = loadable.sampleSize;
sampleData = loadable.sampleData;
loadingFinished = true;
}
@Override
public void onLoadCanceled(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs,
boolean released) {
// Do nothing.
}
@Override
public int onLoadError(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs,
IOException error) {
notifyLoadError(error);
return Loader.RETRY;
}
// Internal methods.
private void notifyLoadError(final IOException e) {
if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() {
@Override
public void run() {
eventListener.onLoadError(eventSourceId, e);
}
});
}
}
private final class SampleStreamImpl implements SampleStream {
private static final int STREAM_STATE_SEND_FORMAT = 0;
private static final int STREAM_STATE_SEND_SAMPLE = 1;
private static final int STREAM_STATE_END_OF_STREAM = 2;
private int streamState;
public void seekToUs(long positionUs) {
if (streamState == STREAM_STATE_END_OF_STREAM) {
streamState = STREAM_STATE_SEND_SAMPLE;
}
}
@Override
public boolean isReady() {
return loadingFinished;
}
@Override
public void maybeThrowError() throws IOException {
loader.maybeThrowError();
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) {
if (streamState == STREAM_STATE_END_OF_STREAM) {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
} else if (streamState == STREAM_STATE_SEND_FORMAT) {
formatHolder.format = format;
streamState = STREAM_STATE_SEND_SAMPLE;
return C.RESULT_FORMAT_READ;
}
Assertions.checkState(streamState == STREAM_STATE_SEND_SAMPLE);
if (!loadingFinished) {
return C.RESULT_NOTHING_READ;
} else {
buffer.timeUs = 0;
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
buffer.ensureSpaceForWrite(sampleSize);
buffer.data.put(sampleData, 0, sampleSize);
streamState = STREAM_STATE_END_OF_STREAM;
return C.RESULT_BUFFER_READ;
}
}
@Override
public void skipToKeyframeBefore(long timeUs) {
// Do nothing.
}
}
/* package */ static final class SourceLoadable implements Loadable {
private final Uri uri;
private final DataSource dataSource;
private int sampleSize;
private byte[] sampleData;
public SourceLoadable(Uri uri, DataSource dataSource) {
this.uri = uri;
this.dataSource = dataSource;
}
@Override
public void cancelLoad() {
// Never happens.
}
@Override
public boolean isLoadCanceled() {
return false;
}
@Override
public void load() throws IOException, InterruptedException {
// We always load from the beginning, so reset the sampleSize to 0.
sampleSize = 0;
try {
// Create and open the input.
dataSource.open(new DataSpec(uri));
// Load the sample data.
int result = 0;
while (result != C.RESULT_END_OF_INPUT) {
sampleSize += result;
if (sampleSize == sampleData.length) {
sampleData = Arrays.copyOf(sampleData, sampleData.length * 2);
}
result = dataSource.read(sampleData, sampleSize, sampleData.length - sampleSize);
}
} finally {
dataSource.close();
}
}
}
}
...@@ -42,25 +42,27 @@ import java.util.List; ...@@ -42,25 +42,27 @@ import java.util.List;
/* package */ final class DashMediaPeriod implements MediaPeriod, /* package */ final class DashMediaPeriod implements MediaPeriod,
SequenceableLoader.Callback<ChunkSampleStream<DashChunkSource>> { SequenceableLoader.Callback<ChunkSampleStream<DashChunkSource>> {
/* package */ final int id;
private final DashChunkSource.Factory chunkSourceFactory; private final DashChunkSource.Factory chunkSourceFactory;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final long elapsedRealtimeOffset; private final long elapsedRealtimeOffset;
private final LoaderErrorThrower manifestLoaderErrorThrower; private final LoaderErrorThrower manifestLoaderErrorThrower;
private final Callback callback;
private final Allocator allocator;
private final TrackGroupArray trackGroups; private final TrackGroupArray trackGroups;
private ChunkSampleStream<DashChunkSource>[] sampleStreams; private ChunkSampleStream<DashChunkSource>[] sampleStreams;
private CompositeSequenceableLoader sequenceableLoader; private CompositeSequenceableLoader sequenceableLoader;
private Callback callback;
private Allocator allocator;
private DashManifest manifest; private DashManifest manifest;
private int index; private int index;
private Period period; private Period period;
public DashMediaPeriod(DashManifest manifest, int index, public DashMediaPeriod(int id, DashManifest manifest, int index,
DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
EventDispatcher eventDispatcher, long elapsedRealtimeOffset, EventDispatcher eventDispatcher, long elapsedRealtimeOffset,
LoaderErrorThrower manifestLoaderErrorThrower) { LoaderErrorThrower manifestLoaderErrorThrower, Callback callback, Allocator allocator) {
this.id = id;
this.manifest = manifest; this.manifest = manifest;
this.index = index; this.index = index;
this.chunkSourceFactory = chunkSourceFactory; this.chunkSourceFactory = chunkSourceFactory;
...@@ -68,8 +70,13 @@ import java.util.List; ...@@ -68,8 +70,13 @@ import java.util.List;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.elapsedRealtimeOffset = elapsedRealtimeOffset; this.elapsedRealtimeOffset = elapsedRealtimeOffset;
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
this.callback = callback;
this.allocator = allocator;
sampleStreams = newSampleStreamArray(0);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
period = manifest.getPeriod(index); period = manifest.getPeriod(index);
trackGroups = buildTrackGroups(period); trackGroups = buildTrackGroups(period);
callback.onPrepared(this);
} }
public void updateManifest(DashManifest manifest, int index) { public void updateManifest(DashManifest manifest, int index) {
...@@ -84,17 +91,6 @@ import java.util.List; ...@@ -84,17 +91,6 @@ import java.util.List;
} }
} }
// MediaPeriod implementation.
@Override
public void preparePeriod(Callback callback, Allocator allocator, long positionUs) {
this.callback = callback;
this.allocator = allocator;
sampleStreams = newSampleStreamArray(0);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
callback.onPeriodPrepared(this);
}
@Override @Override
public void maybeThrowPrepareError() throws IOException { public void maybeThrowPrepareError() throws IOException {
manifestLoaderErrorThrower.maybeThrowError(); manifestLoaderErrorThrower.maybeThrowError();
...@@ -168,19 +164,6 @@ import java.util.List; ...@@ -168,19 +164,6 @@ import java.util.List;
return positionUs; return positionUs;
} }
@Override
public void releasePeriod() {
if (sampleStreams != null) {
for (ChunkSampleStream<DashChunkSource> sampleStream : sampleStreams) {
sampleStream.release();
}
sampleStreams = null;
}
sequenceableLoader = null;
callback = null;
allocator = null;
}
// SequenceableLoader.Callback implementation. // SequenceableLoader.Callback implementation.
@Override @Override
......
...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.ParserException; ...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.SeekWindow; import com.google.android.exoplayer2.source.SeekWindow;
import com.google.android.exoplayer2.source.Timeline; import com.google.android.exoplayer2.source.Timeline;
...@@ -32,6 +33,7 @@ import com.google.android.exoplayer2.source.dash.manifest.DashManifest; ...@@ -32,6 +33,7 @@ import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
import com.google.android.exoplayer2.source.dash.manifest.Period; import com.google.android.exoplayer2.source.dash.manifest.Period;
import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement; import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
...@@ -169,18 +171,29 @@ public final class DashMediaSource implements MediaSource { ...@@ -169,18 +171,29 @@ public final class DashMediaSource implements MediaSource {
} }
@Override @Override
public MediaPeriod createPeriod(int index) throws IOException { public void maybeThrowSourceInfoRefreshError() throws IOException {
if (index >= manifest.getPeriodCount()) { loader.maybeThrowError();
loader.maybeThrowError(); }
return null;
} @Override
DashMediaPeriod mediaPeriod = new DashMediaPeriod(manifest, index, chunkSourceFactory, public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
minLoadableRetryCount, eventDispatcher, elapsedRealtimeOffsetMs, loader); long positionUs) {
periodsById.put(firstPeriodId + index, mediaPeriod); DashMediaPeriod mediaPeriod = new DashMediaPeriod(firstPeriodId + index, manifest, index,
chunkSourceFactory, minLoadableRetryCount, eventDispatcher, elapsedRealtimeOffsetMs, loader,
callback, allocator);
periodsById.put(mediaPeriod.id, mediaPeriod);
return mediaPeriod; return mediaPeriod;
} }
@Override @Override
public void releasePeriod(MediaPeriod mediaPeriod) {
int id = ((DashMediaPeriod) mediaPeriod).id;
if (id >= firstPeriodId) {
periodsById.remove(id);
}
}
@Override
public void releaseSource() { public void releaseSource() {
dataSource = null; dataSource = null;
if (loader != null) { if (loader != null) {
...@@ -246,7 +259,6 @@ public final class DashMediaSource implements MediaSource { ...@@ -246,7 +259,6 @@ public final class DashMediaSource implements MediaSource {
} else { } else {
// Remove old periods. // Remove old periods.
while (periodsToRemoveCount-- > 0) { while (periodsToRemoveCount-- > 0) {
periodsById.remove(firstPeriodId);
firstPeriodId++; firstPeriodId++;
periodCount--; periodCount--;
} }
......
...@@ -46,23 +46,25 @@ import java.util.ArrayList; ...@@ -46,23 +46,25 @@ import java.util.ArrayList;
private final LoaderErrorThrower manifestLoaderErrorThrower; private final LoaderErrorThrower manifestLoaderErrorThrower;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final Callback callback;
private final Allocator allocator;
private final TrackGroupArray trackGroups; private final TrackGroupArray trackGroups;
private final TrackEncryptionBox[] trackEncryptionBoxes; private final TrackEncryptionBox[] trackEncryptionBoxes;
private SsManifest manifest; private SsManifest manifest;
private ChunkSampleStream<SsChunkSource>[] sampleStreams; private ChunkSampleStream<SsChunkSource>[] sampleStreams;
private CompositeSequenceableLoader sequenceableLoader; private CompositeSequenceableLoader sequenceableLoader;
private Callback callback;
private Allocator allocator;
public SsMediaPeriod(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory, public SsMediaPeriod(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory,
int minLoadableRetryCount, EventDispatcher eventDispatcher, int minLoadableRetryCount, EventDispatcher eventDispatcher,
LoaderErrorThrower manifestLoaderErrorThrower) { LoaderErrorThrower manifestLoaderErrorThrower, Callback callback, Allocator allocator) {
this.manifest = manifest;
this.chunkSourceFactory = chunkSourceFactory; this.chunkSourceFactory = chunkSourceFactory;
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.callback = callback;
this.allocator = allocator;
trackGroups = buildTrackGroups(manifest); trackGroups = buildTrackGroups(manifest);
ProtectionElement protectionElement = manifest.protectionElement; ProtectionElement protectionElement = manifest.protectionElement;
if (protectionElement != null) { if (protectionElement != null) {
...@@ -72,25 +74,23 @@ import java.util.ArrayList; ...@@ -72,25 +74,23 @@ import java.util.ArrayList;
} else { } else {
trackEncryptionBoxes = null; trackEncryptionBoxes = null;
} }
this.manifest = manifest;
sampleStreams = newSampleStreamArray(0);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
} }
public void updateManifest(SsManifest manifest) { public void updateManifest(SsManifest manifest) {
this.manifest = manifest; this.manifest = manifest;
if (sampleStreams != null) { for (ChunkSampleStream<SsChunkSource> sampleStream : sampleStreams) {
for (ChunkSampleStream<SsChunkSource> sampleStream : sampleStreams) { sampleStream.getChunkSource().updateManifest(manifest);
sampleStream.getChunkSource().updateManifest(manifest);
}
callback.onContinueLoadingRequested(this);
} }
callback.onContinueLoadingRequested(this);
} }
@Override public void release() {
public void preparePeriod(Callback callback, Allocator allocator, long positionUs) { for (ChunkSampleStream<SsChunkSource> sampleStream : sampleStreams) {
this.callback = callback; sampleStream.release();
this.allocator = allocator; }
sampleStreams = newSampleStreamArray(0);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
callback.onPeriodPrepared(this);
} }
@Override @Override
...@@ -166,19 +166,6 @@ import java.util.ArrayList; ...@@ -166,19 +166,6 @@ import java.util.ArrayList;
return positionUs; return positionUs;
} }
@Override
public void releasePeriod() {
if (sampleStreams != null) {
for (ChunkSampleStream<SsChunkSource> sampleStream : sampleStreams) {
sampleStream.release();
}
sampleStreams = null;
}
sequenceableLoader = null;
callback = null;
allocator = null;
}
// SequenceableLoader.Callback implementation // SequenceableLoader.Callback implementation
@Override @Override
......
...@@ -23,6 +23,7 @@ import com.google.android.exoplayer2.ParserException; ...@@ -23,6 +23,7 @@ import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.SeekWindow; import com.google.android.exoplayer2.source.SeekWindow;
import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.source.SinglePeriodTimeline;
...@@ -30,12 +31,14 @@ import com.google.android.exoplayer2.source.Timeline; ...@@ -30,12 +31,14 @@ import com.google.android.exoplayer2.source.Timeline;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
/** /**
* A SmoothStreaming {@link MediaSource}. * A SmoothStreaming {@link MediaSource}.
...@@ -61,6 +64,7 @@ public final class SsMediaSource implements MediaSource, ...@@ -61,6 +64,7 @@ public final class SsMediaSource implements MediaSource,
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final SsManifestParser manifestParser; private final SsManifestParser manifestParser;
private final ArrayList<SsMediaPeriod> mediaPeriods;
private MediaSource.Listener sourceListener; private MediaSource.Listener sourceListener;
private DataSource manifestDataSource; private DataSource manifestDataSource;
...@@ -71,7 +75,6 @@ public final class SsMediaSource implements MediaSource, ...@@ -71,7 +75,6 @@ public final class SsMediaSource implements MediaSource,
private SeekWindow seekWindow; private SeekWindow seekWindow;
private Handler manifestRefreshHandler; private Handler manifestRefreshHandler;
private SsMediaPeriod period;
public SsMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory, public SsMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
SsChunkSource.Factory chunkSourceFactory, Handler eventHandler, SsChunkSource.Factory chunkSourceFactory, Handler eventHandler,
...@@ -90,6 +93,7 @@ public final class SsMediaSource implements MediaSource, ...@@ -90,6 +93,7 @@ public final class SsMediaSource implements MediaSource,
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener); this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
manifestParser = new SsManifestParser(); manifestParser = new SsManifestParser();
mediaPeriods = new ArrayList<>();
} }
// MediaSource implementation. // MediaSource implementation.
...@@ -122,15 +126,29 @@ public final class SsMediaSource implements MediaSource, ...@@ -122,15 +126,29 @@ public final class SsMediaSource implements MediaSource,
} }
@Override @Override
public MediaPeriod createPeriod(int index) { public void maybeThrowSourceInfoRefreshError() throws IOException {
manifestLoader.maybeThrowError();
}
@Override
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
long positionUs) {
Assertions.checkArgument(index == 0); Assertions.checkArgument(index == 0);
SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount,
eventDispatcher, manifestLoader, callback, allocator);
mediaPeriods.add(period);
return period; return period;
} }
@Override @Override
public void releasePeriod(MediaPeriod period) {
((SsMediaPeriod) period).release();
mediaPeriods.remove(period);
}
@Override
public void releaseSource() { public void releaseSource() {
sourceListener = null; sourceListener = null;
period = null;
manifest = null; manifest = null;
manifestDataSource = null; manifestDataSource = null;
manifestLoadStartTimestamp = 0; manifestLoadStartTimestamp = 0;
...@@ -153,11 +171,8 @@ public final class SsMediaSource implements MediaSource, ...@@ -153,11 +171,8 @@ public final class SsMediaSource implements MediaSource,
loadDurationMs, loadable.bytesLoaded()); loadDurationMs, loadable.bytesLoaded());
manifest = loadable.getResult(); manifest = loadable.getResult();
manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs; manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs;
if (period == null) { for (int i = 0; i < mediaPeriods.size(); i++) {
period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount, mediaPeriods.get(i).updateManifest(manifest);
eventDispatcher, manifestLoader);
} else {
period.updateManifest(manifest);
} }
Timeline timeline; Timeline timeline;
if (manifest.isLive) { if (manifest.isLive) {
...@@ -175,9 +190,9 @@ public final class SsMediaSource implements MediaSource, ...@@ -175,9 +190,9 @@ public final class SsMediaSource implements MediaSource,
SeekWindow.createWindow(0, startTimeUs, 0, startTimeUs + manifest.dvrWindowLengthUs)); SeekWindow.createWindow(0, startTimeUs, 0, startTimeUs + manifest.dvrWindowLengthUs));
} }
} else if (manifest.durationUs == C.UNSET_TIME_US) { } else if (manifest.durationUs == C.UNSET_TIME_US) {
timeline = SinglePeriodTimeline.createUnseekableFinalTimeline(this, C.UNSET_TIME_US); timeline = SinglePeriodTimeline.createUnseekableFinalTimeline(0, C.UNSET_TIME_US);
} else { } else {
timeline = SinglePeriodTimeline.createSeekableFinalTimeline(this, manifest.durationUs); timeline = SinglePeriodTimeline.createSeekableFinalTimeline(0, manifest.durationUs);
} }
seekWindow = timeline.getSeekWindow(0); seekWindow = timeline.getSeekWindow(0);
sourceListener.onSourceInfoRefreshed(timeline, manifest); sourceListener.onSourceInfoRefreshed(timeline, manifest);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment