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;
// MediaPeriod.Callback implementation.
@Override
public void onPeriodPrepared(MediaPeriod source) {
public void onPrepared(MediaPeriod source) {
handler.obtainMessage(MSG_PERIOD_PREPARED, source).sendToTarget();
}
......@@ -906,6 +906,7 @@ import java.io.IOException;
public void updatePeriods() throws ExoPlaybackException, IOException {
if (timeline == null) {
// We're waiting to get information about periods.
mediaSource.maybeThrowSourceInfoRefreshError();
return;
}
......@@ -927,11 +928,14 @@ import java.io.IOException;
}
}
MediaPeriod mediaPeriod;
if (startPositionUs != C.UNSET_TIME_US
&& (mediaPeriod = mediaSource.createPeriod(periodIndex)) != null) {
Period newPeriod = new Period(renderers, rendererCapabilities, trackSelector, mediaPeriod,
timeline.getPeriodId(periodIndex), periodIndex, startPositionUs);
if (periodIndex >= timeline.getPeriodCount()) {
// This period is not available yet.
mediaSource.maybeThrowSourceInfoRefreshError();
} else if (startPositionUs != C.UNSET_TIME_US) {
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;
if (loadingPeriod != null) {
loadingPeriod.setNextPeriod(newPeriod);
......@@ -941,7 +945,6 @@ import java.io.IOException;
bufferAheadPeriodCount++;
loadingPeriod = newPeriod;
setIsLoading(true);
loadingPeriod.mediaPeriod.preparePeriod(this, loadControl.getAllocator(), startPositionUs);
}
}
......@@ -1169,17 +1172,19 @@ import java.io.IOException;
private final Renderer[] renderers;
private final RendererCapabilities[] rendererCapabilities;
private final TrackSelector trackSelector;
private final MediaSource mediaSource;
private Object trackSelectionData;
private TrackSelectionArray trackSelections;
private TrackSelectionArray periodTrackSelections;
public Period(Renderer[] renderers, RendererCapabilities[] rendererCapabilities,
TrackSelector trackSelector, MediaPeriod mediaPeriod, Object id, int index,
long positionUs) {
TrackSelector trackSelector, MediaSource mediaSource, MediaPeriod mediaPeriod, Object id,
int index, long positionUs) {
this.renderers = renderers;
this.rendererCapabilities = rendererCapabilities;
this.trackSelector = trackSelector;
this.mediaSource = mediaSource;
this.mediaPeriod = mediaPeriod;
this.id = Assertions.checkNotNull(id);
sampleStreams = new SampleStream[renderers.length];
......@@ -1250,7 +1255,7 @@ import java.io.IOException;
public void release() {
try {
mediaPeriod.releasePeriod();
mediaSource.releasePeriod(mediaPeriod);
} catch (RuntimeException e) {
// There's nothing we can do.
Log.e(TAG, "Period release failed.", e);
......
......@@ -16,9 +16,13 @@
package com.google.android.exoplayer2.source;
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 java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Concatenates multiple {@link MediaSource}s.
......@@ -28,6 +32,7 @@ public final class ConcatenatingMediaSource implements MediaSource {
private final MediaSource[] mediaSources;
private final Timeline[] timelines;
private final Object[] manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private ConcatenatedTimeline timeline;
......@@ -38,6 +43,7 @@ public final class ConcatenatingMediaSource implements MediaSource {
this.mediaSources = mediaSources;
timelines = new Timeline[mediaSources.length];
manifests = new Object[mediaSources.length];
sourceIndexByMediaPeriod = new HashMap<>();
}
@Override
......@@ -85,10 +91,28 @@ public final class ConcatenatingMediaSource implements MediaSource {
}
@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 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
......
......@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException;
/**
......@@ -39,24 +38,11 @@ public interface MediaPeriod extends SequenceableLoader {
*
* @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
* error exists.
* <p>
......@@ -136,12 +122,4 @@ public interface MediaPeriod extends SequenceableLoader {
*/
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 @@
*/
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;
/**
......@@ -105,16 +107,31 @@ public interface MediaSource {
Position getDefaultStartPosition(int index);
/**
* Returns a {@link MediaPeriod} corresponding to the period at the specified index, or
* {@code null} if the period at the specified index is not yet available.
* Throws any pending error encountered while loading or refreshing source information.
*/
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.
* @return A {@link MediaPeriod}, or {@code null} if the source at the specified index is not
* available.
* @throws IOException If there is an error that's preventing the source from becoming prepared or
* creating periods.
* @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.
* @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.
......
......@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.IdentityHashMap;
......@@ -25,30 +24,23 @@ import java.util.IdentityHashMap;
/**
* 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 Callback callback;
private int pendingChildPrepareCount;
private TrackGroupArray trackGroups;
private MediaPeriod[] enabledPeriods;
private SequenceableLoader sequenceableLoader;
public MergingMediaPeriod(MediaPeriod... periods) {
public MergingMediaPeriod(Callback callback, MediaPeriod... periods) {
this.periods = periods;
pendingChildPrepareCount = periods.length;
streamPeriodIndices = new IdentityHashMap<>();
}
@Override
public void preparePeriod(Callback callback, Allocator allocator, long positionUs) {
this.callback = callback;
for (MediaPeriod period : periods) {
period.preparePeriod(this, allocator, positionUs);
}
streamPeriodIndices = new IdentityHashMap<>();
pendingChildPrepareCount = periods.length;
}
@Override
......@@ -174,17 +166,10 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba
return positionUs;
}
@Override
public void releasePeriod() {
for (MediaPeriod period : periods) {
period.releasePeriod();
}
}
// MediaPeriod.Callback implementation
@Override
public void onPeriodPrepared(MediaPeriod ignored) {
public void onPrepared(MediaPeriod ignored) {
if (--pendingChildPrepareCount > 0) {
return;
}
......@@ -202,7 +187,7 @@ public final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callba
}
}
trackGroups = new TrackGroupArray(trackGroupArray);
callback.onPeriodPrepared(this);
callback.onPrepared(this);
}
@Override
......
......@@ -15,6 +15,8 @@
*/
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 java.io.IOException;
......@@ -73,13 +75,31 @@ public final class MergingMediaSource implements MediaSource {
}
@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];
// 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++) {
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");
}
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
......
/*
* 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;
/* package */ final class DashMediaPeriod implements MediaPeriod,
SequenceableLoader.Callback<ChunkSampleStream<DashChunkSource>> {
/* package */ final int id;
private final DashChunkSource.Factory chunkSourceFactory;
private final int minLoadableRetryCount;
private final EventDispatcher eventDispatcher;
private final long elapsedRealtimeOffset;
private final LoaderErrorThrower manifestLoaderErrorThrower;
private final Callback callback;
private final Allocator allocator;
private final TrackGroupArray trackGroups;
private ChunkSampleStream<DashChunkSource>[] sampleStreams;
private CompositeSequenceableLoader sequenceableLoader;
private Callback callback;
private Allocator allocator;
private DashManifest manifest;
private int index;
private Period period;
public DashMediaPeriod(DashManifest manifest, int index,
public DashMediaPeriod(int id, DashManifest manifest, int index,
DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
EventDispatcher eventDispatcher, long elapsedRealtimeOffset,
LoaderErrorThrower manifestLoaderErrorThrower) {
LoaderErrorThrower manifestLoaderErrorThrower, Callback callback, Allocator allocator) {
this.id = id;
this.manifest = manifest;
this.index = index;
this.chunkSourceFactory = chunkSourceFactory;
......@@ -68,8 +70,13 @@ import java.util.List;
this.eventDispatcher = eventDispatcher;
this.elapsedRealtimeOffset = elapsedRealtimeOffset;
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
this.callback = callback;
this.allocator = allocator;
sampleStreams = newSampleStreamArray(0);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
period = manifest.getPeriod(index);
trackGroups = buildTrackGroups(period);
callback.onPrepared(this);
}
public void updateManifest(DashManifest manifest, int index) {
......@@ -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
public void maybeThrowPrepareError() throws IOException {
manifestLoaderErrorThrower.maybeThrowError();
......@@ -168,19 +164,6 @@ import java.util.List;
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.
@Override
......
......@@ -25,6 +25,7 @@ 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.MediaPeriod;
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.SeekWindow;
import com.google.android.exoplayer2.source.Timeline;
......@@ -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.Period;
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.Loader;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
......@@ -169,18 +171,29 @@ public final class DashMediaSource implements MediaSource {
}
@Override
public MediaPeriod createPeriod(int index) throws IOException {
if (index >= manifest.getPeriodCount()) {
loader.maybeThrowError();
return null;
}
DashMediaPeriod mediaPeriod = new DashMediaPeriod(manifest, index, chunkSourceFactory,
minLoadableRetryCount, eventDispatcher, elapsedRealtimeOffsetMs, loader);
periodsById.put(firstPeriodId + index, mediaPeriod);
public void maybeThrowSourceInfoRefreshError() throws IOException {
loader.maybeThrowError();
}
@Override
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
long positionUs) {
DashMediaPeriod mediaPeriod = new DashMediaPeriod(firstPeriodId + index, manifest, index,
chunkSourceFactory, minLoadableRetryCount, eventDispatcher, elapsedRealtimeOffsetMs, loader,
callback, allocator);
periodsById.put(mediaPeriod.id, mediaPeriod);
return mediaPeriod;
}
@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
int id = ((DashMediaPeriod) mediaPeriod).id;
if (id >= firstPeriodId) {
periodsById.remove(id);
}
}
@Override
public void releaseSource() {
dataSource = null;
if (loader != null) {
......@@ -246,7 +259,6 @@ public final class DashMediaSource implements MediaSource {
} else {
// Remove old periods.
while (periodsToRemoveCount-- > 0) {
periodsById.remove(firstPeriodId);
firstPeriodId++;
periodCount--;
}
......
......@@ -46,23 +46,25 @@ import java.util.ArrayList;
private final LoaderErrorThrower manifestLoaderErrorThrower;
private final int minLoadableRetryCount;
private final EventDispatcher eventDispatcher;
private final Callback callback;
private final Allocator allocator;
private final TrackGroupArray trackGroups;
private final TrackEncryptionBox[] trackEncryptionBoxes;
private SsManifest manifest;
private ChunkSampleStream<SsChunkSource>[] sampleStreams;
private CompositeSequenceableLoader sequenceableLoader;
private Callback callback;
private Allocator allocator;
public SsMediaPeriod(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory,
int minLoadableRetryCount, EventDispatcher eventDispatcher,
LoaderErrorThrower manifestLoaderErrorThrower) {
this.manifest = manifest;
LoaderErrorThrower manifestLoaderErrorThrower, Callback callback, Allocator allocator) {
this.chunkSourceFactory = chunkSourceFactory;
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
this.minLoadableRetryCount = minLoadableRetryCount;
this.eventDispatcher = eventDispatcher;
this.callback = callback;
this.allocator = allocator;
trackGroups = buildTrackGroups(manifest);
ProtectionElement protectionElement = manifest.protectionElement;
if (protectionElement != null) {
......@@ -72,25 +74,23 @@ import java.util.ArrayList;
} else {
trackEncryptionBoxes = null;
}
this.manifest = manifest;
sampleStreams = newSampleStreamArray(0);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
}
public void updateManifest(SsManifest manifest) {
this.manifest = manifest;
if (sampleStreams != null) {
for (ChunkSampleStream<SsChunkSource> sampleStream : sampleStreams) {
sampleStream.getChunkSource().updateManifest(manifest);
}
callback.onContinueLoadingRequested(this);
for (ChunkSampleStream<SsChunkSource> sampleStream : sampleStreams) {
sampleStream.getChunkSource().updateManifest(manifest);
}
callback.onContinueLoadingRequested(this);
}
@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);
public void release() {
for (ChunkSampleStream<SsChunkSource> sampleStream : sampleStreams) {
sampleStream.release();
}
}
@Override
......@@ -166,19 +166,6 @@ import java.util.ArrayList;
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
@Override
......
......@@ -23,6 +23,7 @@ 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.MediaPeriod;
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.SeekWindow;
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
......@@ -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.StreamElement;
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.Loader;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.ArrayList;
/**
* A SmoothStreaming {@link MediaSource}.
......@@ -61,6 +64,7 @@ public final class SsMediaSource implements MediaSource,
private final int minLoadableRetryCount;
private final EventDispatcher eventDispatcher;
private final SsManifestParser manifestParser;
private final ArrayList<SsMediaPeriod> mediaPeriods;
private MediaSource.Listener sourceListener;
private DataSource manifestDataSource;
......@@ -71,7 +75,6 @@ public final class SsMediaSource implements MediaSource,
private SeekWindow seekWindow;
private Handler manifestRefreshHandler;
private SsMediaPeriod period;
public SsMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
SsChunkSource.Factory chunkSourceFactory, Handler eventHandler,
......@@ -90,6 +93,7 @@ public final class SsMediaSource implements MediaSource,
this.minLoadableRetryCount = minLoadableRetryCount;
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
manifestParser = new SsManifestParser();
mediaPeriods = new ArrayList<>();
}
// MediaSource implementation.
......@@ -122,15 +126,29 @@ public final class SsMediaSource implements MediaSource,
}
@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);
SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount,
eventDispatcher, manifestLoader, callback, allocator);
mediaPeriods.add(period);
return period;
}
@Override
public void releasePeriod(MediaPeriod period) {
((SsMediaPeriod) period).release();
mediaPeriods.remove(period);
}
@Override
public void releaseSource() {
sourceListener = null;
period = null;
manifest = null;
manifestDataSource = null;
manifestLoadStartTimestamp = 0;
......@@ -153,11 +171,8 @@ public final class SsMediaSource implements MediaSource,
loadDurationMs, loadable.bytesLoaded());
manifest = loadable.getResult();
manifestLoadStartTimestamp = elapsedRealtimeMs - loadDurationMs;
if (period == null) {
period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount,
eventDispatcher, manifestLoader);
} else {
period.updateManifest(manifest);
for (int i = 0; i < mediaPeriods.size(); i++) {
mediaPeriods.get(i).updateManifest(manifest);
}
Timeline timeline;
if (manifest.isLive) {
......@@ -175,9 +190,9 @@ public final class SsMediaSource implements MediaSource,
SeekWindow.createWindow(0, startTimeUs, 0, startTimeUs + manifest.dvrWindowLengthUs));
}
} 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 {
timeline = SinglePeriodTimeline.createSeekableFinalTimeline(this, manifest.durationUs);
timeline = SinglePeriodTimeline.createSeekableFinalTimeline(0, manifest.durationUs);
}
seekWindow = timeline.getSeekWindow(0);
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