From af3452d231c904600bf9e2c08f3f59565b2683fe Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 8 Jul 2016 00:57:45 -0700 Subject: [PATCH] Make SampleSources reusable and implement SampleSourceProvider. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=126888556 --- .../exoplayer/demo/PlayerActivity.java | 36 ++++- .../demo/UriSampleSourceProvider.java | 105 ------------ .../exoplayer/ext/flac/FlacPlaybackTest.java | 2 +- .../exoplayer/ext/opus/OpusPlaybackTest.java | 2 +- .../exoplayer/ext/vp9/VpxPlaybackTest.java | 2 +- .../google/android/exoplayer/ExoPlayer.java | 8 - .../android/exoplayer/ExoPlayerImpl.java | 26 --- .../android/exoplayer/SimpleExoPlayer.java | 5 - .../android/exoplayer/SingleSampleSource.java | 149 +++++++++++------- .../exoplayer/dash/DashSampleSource.java | 66 ++++++-- .../extractor/ExtractorSampleSource.java | 85 +++++++--- .../exoplayer/hls/HlsSampleSource.java | 65 ++++++-- .../SmoothStreamingSampleSource.java | 65 ++++++-- .../exoplayer/playbacktests/gts/DashTest.java | 5 +- .../playbacktests/util/ExoHostedTest.java | 6 +- 15 files changed, 354 insertions(+), 273 deletions(-) delete mode 100644 demo/src/main/java/com/google/android/exoplayer/demo/UriSampleSourceProvider.java diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index ffac3b506f..359f41a8d8 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -30,18 +30,24 @@ import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.SampleSourceProvider; import com.google.android.exoplayer.SimpleExoPlayer; import com.google.android.exoplayer.TrackGroupArray; +import com.google.android.exoplayer.dash.DashSampleSource; import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.drm.UnsupportedDrmException; +import com.google.android.exoplayer.extractor.DefaultExtractorsFactory; +import com.google.android.exoplayer.extractor.ExtractorSampleSource; +import com.google.android.exoplayer.hls.HlsSampleSource; import com.google.android.exoplayer.metadata.id3.ApicFrame; import com.google.android.exoplayer.metadata.id3.GeobFrame; import com.google.android.exoplayer.metadata.id3.Id3Frame; import com.google.android.exoplayer.metadata.id3.PrivFrame; import com.google.android.exoplayer.metadata.id3.TextInformationFrame; import com.google.android.exoplayer.metadata.id3.TxxxFrame; +import com.google.android.exoplayer.smoothstreaming.SmoothStreamingSampleSource; import com.google.android.exoplayer.text.CaptionStyleCompat; import com.google.android.exoplayer.text.Cue; import com.google.android.exoplayer.text.SubtitleLayout; +import com.google.android.exoplayer.upstream.BandwidthMeter; import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer.util.DebugTextViewHelper; @@ -57,6 +63,7 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -126,6 +133,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, private DefaultTrackSelector trackSelector; private TrackSelectionHelper trackSelectionHelper; private DebugTextViewHelper debugViewHelper; + private BandwidthMeter bandwidthMeter; private boolean playerNeedsSource; private long playerPosition; @@ -283,6 +291,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, mediaController.setAnchorView(rootView); debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper.start(); + bandwidthMeter = player.getBandwidthMeter(); playerNeedsSource = true; } if (playerNeedsSource) { @@ -311,10 +320,9 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, return; } - UriSampleSourceProvider[] providers = new UriSampleSourceProvider[uris.length]; + SampleSourceProvider[] providers = new SampleSourceProvider[uris.length]; for (int i = 0; i < uris.length; i++) { - providers[i] = new UriSampleSourceProvider(player.getBandwidthMeter(), dataSourceFactory, - uris[i], extensions[i], mainHandler, eventLogger); + providers[i] = getSampleSourceProvider(uris[i], extensions[i]); } SampleSourceProvider sourceProvider = providers.length == 1 ? providers[0] : new ConcatenatingSampleSourceProvider(providers); @@ -324,6 +332,28 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, } } + private SampleSourceProvider getSampleSourceProvider(Uri uri, String overrideExtension) { + String lastPathSegment = !TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension + : uri.getLastPathSegment(); + int type = Util.inferContentType(lastPathSegment); + switch (type) { + case Util.TYPE_SS: + return new SmoothStreamingSampleSource(uri, dataSourceFactory, bandwidthMeter, mainHandler, + eventLogger); + case Util.TYPE_DASH: + return new DashSampleSource(uri, dataSourceFactory, bandwidthMeter, mainHandler, + eventLogger); + case Util.TYPE_HLS: + return new HlsSampleSource(uri, dataSourceFactory, bandwidthMeter, mainHandler, + eventLogger); + case Util.TYPE_OTHER: + return new ExtractorSampleSource(uri, dataSourceFactory, bandwidthMeter, + new DefaultExtractorsFactory(), mainHandler, eventLogger); + default: + throw new IllegalStateException("Unsupported type: " + type); + } + } + private DrmSessionManager buildDrmSessionManager(UUID uuid, String id, String provider) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/UriSampleSourceProvider.java b/demo/src/main/java/com/google/android/exoplayer/demo/UriSampleSourceProvider.java deleted file mode 100644 index b89c4c0fd3..0000000000 --- a/demo/src/main/java/com/google/android/exoplayer/demo/UriSampleSourceProvider.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2014 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.exoplayer.demo; - -import com.google.android.exoplayer.SampleSource; -import com.google.android.exoplayer.SampleSourceProvider; -import com.google.android.exoplayer.dash.DashSampleSource; -import com.google.android.exoplayer.extractor.DefaultExtractorsFactory; -import com.google.android.exoplayer.extractor.ExtractorSampleSource; -import com.google.android.exoplayer.hls.HlsSampleSource; -import com.google.android.exoplayer.smoothstreaming.SmoothStreamingSampleSource; -import com.google.android.exoplayer.upstream.BandwidthMeter; -import com.google.android.exoplayer.upstream.DataSourceFactory; -import com.google.android.exoplayer.util.Util; - -import android.net.Uri; -import android.os.Handler; -import android.text.TextUtils; - -/** - * Provides a {@link SampleSource} to play back media loaded from a {@link Uri}. - */ -public final class UriSampleSourceProvider implements SampleSourceProvider { - - private final BandwidthMeter bandwidthMeter; - private final DataSourceFactory dataSourceFactory; - private final Uri uri; - private final String overrideExtension; - private final Handler handler; - private final EventLogger eventLogger; - - /** - * Constructs a source provider for {@link SampleSource} to play back media at the specified - * URI, using the specified type. - * - * @param bandwidthMeter A bandwidth meter. - * @param dataSourceFactory A data source factory. - * @param uri The URI to play back. - * @param overrideExtension An overriding file extension used when inferring the source's type, - * or {@code null}. - * @param handler A handler to use for logging events. - * @param eventLogger An event logger. - */ - public UriSampleSourceProvider(BandwidthMeter bandwidthMeter, DataSourceFactory dataSourceFactory, - Uri uri, String overrideExtension, Handler handler, EventLogger eventLogger) { - this.bandwidthMeter = bandwidthMeter; - this.dataSourceFactory = dataSourceFactory; - this.uri = uri; - this.overrideExtension = overrideExtension; - this.handler = handler; - this.eventLogger = eventLogger; - } - - @Override - public int getSourceCount() { - return 1; - } - - @Override - public SampleSource createSource(int index) { - int type = inferContentType(uri, overrideExtension); - switch (type) { - case Util.TYPE_SS: - return new SmoothStreamingSampleSource(uri, dataSourceFactory, bandwidthMeter, handler, - eventLogger); - case Util.TYPE_DASH: - return new DashSampleSource(uri, dataSourceFactory, bandwidthMeter, handler, eventLogger); - case Util.TYPE_HLS: - return new HlsSampleSource(uri, dataSourceFactory, bandwidthMeter, handler, eventLogger); - case Util.TYPE_OTHER: - return new ExtractorSampleSource(uri, dataSourceFactory, bandwidthMeter, - new DefaultExtractorsFactory(), handler, eventLogger); - default: - throw new IllegalStateException("Unsupported type: " + type); - } - } - - /** - * Makes a best guess to infer the type from a media {@link Uri} and an optional overriding file - * extension. - * - * @param uri The {@link Uri} of the media. - * @param fileExtension An overriding file extension. - * @return The inferred type. - */ - private static int inferContentType(Uri uri, String fileExtension) { - String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension - : uri.getLastPathSegment(); - return Util.inferContentType(lastPathSegment); - } - -} diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer/ext/flac/FlacPlaybackTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer/ext/flac/FlacPlaybackTest.java index 6b6dc19f73..6c4ea7e065 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer/ext/flac/FlacPlaybackTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer/ext/flac/FlacPlaybackTest.java @@ -83,7 +83,7 @@ public class FlacPlaybackTest extends InstrumentationTestCase { new MatroskaExtractor.Factory(), null, null); - player.setSource(sampleSource); + player.setSourceProvider(sampleSource); player.setPlayWhenReady(true); Looper.loop(); } diff --git a/extensions/opus/src/androidTest/java/com/google/android/exoplayer/ext/opus/OpusPlaybackTest.java b/extensions/opus/src/androidTest/java/com/google/android/exoplayer/ext/opus/OpusPlaybackTest.java index 6167ade9f9..4ab3ecab60 100644 --- a/extensions/opus/src/androidTest/java/com/google/android/exoplayer/ext/opus/OpusPlaybackTest.java +++ b/extensions/opus/src/androidTest/java/com/google/android/exoplayer/ext/opus/OpusPlaybackTest.java @@ -83,7 +83,7 @@ public class OpusPlaybackTest extends InstrumentationTestCase { new MatroskaExtractor.Factory(), null, null); - player.setSource(sampleSource); + player.setSourceProvider(sampleSource); player.setPlayWhenReady(true); Looper.loop(); } diff --git a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer/ext/vp9/VpxPlaybackTest.java b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer/ext/vp9/VpxPlaybackTest.java index af4e195f77..9def565f30 100644 --- a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer/ext/vp9/VpxPlaybackTest.java +++ b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer/ext/vp9/VpxPlaybackTest.java @@ -102,7 +102,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase { player.sendMessages(new ExoPlayer.ExoPlayerMessage(videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER, new VpxVideoSurfaceView(context))); - player.setSource(sampleSource); + player.setSourceProvider(sampleSource); player.setPlayWhenReady(true); Looper.loop(); } diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayer.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayer.java index 3397e75df2..75dda93f8c 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayer.java @@ -235,14 +235,6 @@ public interface ExoPlayer { */ int getPlaybackState(); - /** - * Sets the player's source. The player will transition to {@link #STATE_BUFFERING} until it is - * ready to play the new source. - * - * @param sampleSource The {@link SampleSource} to play. - */ - void setSource(SampleSource sampleSource); - /** * Sets the player's source provider. The player's position will be reset to the start of the * first source and the player will transition to {@link #STATE_BUFFERING} until it is ready to diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java index 8c5d783086..9fe0283df9 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java @@ -93,11 +93,6 @@ import java.util.concurrent.CopyOnWriteArraySet; return playbackState; } - @Override - public void setSource(final SampleSource sampleSource) { - setSourceProvider(new SingleSampleSourceProvider(sampleSource)); - } - @Override public void setSourceProvider(SampleSourceProvider sourceProvider) { maskingSourceIndex = 0; @@ -267,25 +262,4 @@ import java.util.concurrent.CopyOnWriteArraySet; } } - private static final class SingleSampleSourceProvider implements SampleSourceProvider { - - private final SampleSource sampleSource; - - public SingleSampleSourceProvider(SampleSource sampleSource) { - this.sampleSource = sampleSource; - } - - @Override - public int getSourceCount() { - return 1; - } - - @Override - public SampleSource createSource(int index) { - // The source will only be created once. - return sampleSource; - } - - } - } diff --git a/library/src/main/java/com/google/android/exoplayer/SimpleExoPlayer.java b/library/src/main/java/com/google/android/exoplayer/SimpleExoPlayer.java index b7f14b8531..f2fae30a3f 100644 --- a/library/src/main/java/com/google/android/exoplayer/SimpleExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer/SimpleExoPlayer.java @@ -313,11 +313,6 @@ public final class SimpleExoPlayer implements ExoPlayer { return player.getPlaybackState(); } - @Override - public void setSource(SampleSource sampleSource) { - player.setSource(sampleSource); - } - @Override public void setSourceProvider(SampleSourceProvider sourceProvider) { player.setSourceProvider(sourceProvider); diff --git a/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java b/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java index b4bf769791..99cd358f64 100644 --- a/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.upstream.Allocator; import com.google.android.exoplayer.upstream.DataSource; +import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader.Loadable; @@ -30,10 +31,11 @@ import java.util.Arrays; import java.util.List; /** - * A {@link SampleSource} that loads the data at a given {@link Uri} as a single sample. + * A {@link SampleSource} that loads the data at a given {@link Uri} as a single sample. Also acts + * as a {@link SampleSourceProvider} providing {@link SingleSampleSource} instances. */ -public final class SingleSampleSource implements SampleSource, TrackStream, - Loader.Callback, Loadable { +public final class SingleSampleSource implements SampleSource, SampleSourceProvider, TrackStream, + Loader.Callback { /** * Interface definition for a callback to be notified of {@link SingleSampleSource} events. @@ -65,8 +67,7 @@ public final class SingleSampleSource implements SampleSource, TrackStream, private static final int STREAM_STATE_END_OF_STREAM = 2; private final Uri uri; - private final DataSource dataSource; - private final Loader loader; + private final DataSourceFactory dataSourceFactory; private final Format format; private final long durationUs; private final int minLoadableRetryCount; @@ -75,41 +76,57 @@ public final class SingleSampleSource implements SampleSource, TrackStream, private final EventListener eventListener; private final int eventSourceId; + private Loader loader; private boolean loadingFinished; private int streamState; private byte[] sampleData; private int sampleSize; - public SingleSampleSource(Uri uri, DataSource dataSource, Format format, long durationUs) { - this(uri, dataSource, format, durationUs, DEFAULT_MIN_LOADABLE_RETRY_COUNT); + public SingleSampleSource(Uri uri, DataSourceFactory dataSourceFactory, Format format, + long durationUs) { + this(uri, dataSourceFactory, format, durationUs, DEFAULT_MIN_LOADABLE_RETRY_COUNT); } - public SingleSampleSource(Uri uri, DataSource dataSource, Format format, long durationUs, - int minLoadableRetryCount) { - this(uri, dataSource, format, durationUs, minLoadableRetryCount, null, null, 0); + public SingleSampleSource(Uri uri, DataSourceFactory dataSourceFactory, Format format, + long durationUs, int minLoadableRetryCount) { + this(uri, dataSourceFactory, format, durationUs, minLoadableRetryCount, null, null, 0); } - public SingleSampleSource(Uri uri, DataSource dataSource, Format format, long durationUs, - int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, + public SingleSampleSource(Uri uri, DataSourceFactory dataSourceFactory, Format format, + long durationUs, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, int eventSourceId) { this.uri = uri; - this.dataSource = dataSource; + this.dataSourceFactory = dataSourceFactory; this.format = format; this.durationUs = durationUs; this.minLoadableRetryCount = minLoadableRetryCount; this.eventHandler = eventHandler; this.eventListener = eventListener; this.eventSourceId = eventSourceId; - loader = new Loader("Loader:SingleSampleSource"); tracks = new TrackGroupArray(new TrackGroup(format)); sampleData = new byte[INITIAL_SAMPLE_SIZE]; + streamState = STREAM_STATE_SEND_FORMAT; + } + + // SampleSourceProvider implementation. + + @Override + public int getSourceCount() { + return 1; + } + + @Override + public SampleSource createSource(int index) { + Assertions.checkArgument(index == 0); + return this; } // SampleSource implementation. @Override public void prepare(Callback callback, Allocator allocator, long positionUs) { + loader = new Loader("Loader:SingleSampleSource"); callback.onSourcePrepared(this); } @@ -147,7 +164,8 @@ public final class SingleSampleSource implements SampleSource, TrackStream, if (loadingFinished || loader.isLoading()) { return false; } - loader.startLoading(this, this, minLoadableRetryCount); + loader.startLoading(new SourceLoadable(uri, dataSourceFactory.createDataSource()), this, + minLoadableRetryCount); return true; } @@ -171,8 +189,14 @@ public final class SingleSampleSource implements SampleSource, TrackStream, @Override public void release() { + if (loader != null) { + loader.release(); + loader = null; + } + loadingFinished = false; + streamState = STREAM_STATE_SEND_FORMAT; sampleData = null; - loader.release(); + sampleSize = 0; } // TrackStream implementation. @@ -219,57 +243,26 @@ public final class SingleSampleSource implements SampleSource, TrackStream, // Loader.Callback implementation. @Override - public void onLoadCompleted(SingleSampleSource loadable, long elapsedRealtimeMs, + public void onLoadCompleted(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { + sampleSize = loadable.sampleSize; + sampleData = loadable.sampleData; loadingFinished = true; } @Override - public void onLoadCanceled(SingleSampleSource loadable, long elapsedRealtimeMs, - long loadDurationMs, boolean released) { - // Never happens. + public void onLoadCanceled(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, + boolean released) { + // Do nothing. } @Override - public int onLoadError(SingleSampleSource loadable, long elapsedRealtimeMs, - long loadDurationMs, IOException error) { + public int onLoadError(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, + IOException error) { notifyLoadError(error); return Loader.RETRY; } - // Loadable implementation. - - @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(); - } - } - // Internal methods. private void notifyLoadError(final IOException e) { @@ -283,4 +276,50 @@ public final class SingleSampleSource implements SampleSource, TrackStream, } } + /* 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(); + } + } + + } + } diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java index cf5cba5954..dfe4748c46 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer.CompositeSequenceableLoader; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.SampleSource; +import com.google.android.exoplayer.SampleSourceProvider; import com.google.android.exoplayer.SequenceableLoader; import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroupArray; @@ -42,6 +43,7 @@ import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.ParsingLoadable; +import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Util; import android.net.Uri; @@ -61,9 +63,10 @@ import java.util.Locale; import java.util.TimeZone; /** - * A {@link SampleSource} for DASH media. + * A {@link SampleSource} for DASH media. Also acts as a {@link SampleSourceProvider} providing + * {@link DashSampleSource} instances. */ -public final class DashSampleSource implements SampleSource, +public final class DashSampleSource implements SampleSource, SampleSourceProvider, SequenceableLoader.Callback> { /** @@ -77,11 +80,14 @@ public final class DashSampleSource implements SampleSource, private final BandwidthMeter bandwidthMeter; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; - private final Loader loader; - private final DataSource dataSource; private final MediaPresentationDescriptionParser manifestParser; private final ManifestCallback manifestCallback; + private DataSource dataSource; + private Loader loader; + private ChunkTrackStream[] trackStreams; + private CompositeSequenceableLoader sequenceableLoader; + private Uri manifestUri; private long manifestLoadStartTimestamp; private long manifestLoadEndTimestamp; @@ -96,9 +102,6 @@ public final class DashSampleSource implements SampleSource, private TrackGroupArray trackGroups; private int[] trackGroupAdaptationSetIndices; - private ChunkTrackStream[] trackStreams; - private CompositeSequenceableLoader sequenceableLoader; - public DashSampleSource(Uri manifestUri, DataSourceFactory dataSourceFactory, BandwidthMeter bandwidthMeter, Handler eventHandler, AdaptiveSourceEventListener eventListener) { @@ -114,18 +117,33 @@ public final class DashSampleSource implements SampleSource, this.bandwidthMeter = bandwidthMeter; this.minLoadableRetryCount = minLoadableRetryCount; eventDispatcher = new EventDispatcher(eventHandler, eventListener); - dataSource = dataSourceFactory.createDataSource(); - loader = new Loader("Loader:DashSampleSource"); manifestParser = new MediaPresentationDescriptionParser(); manifestCallback = new ManifestCallback(); - trackStreams = newTrackStreamArray(0); - sequenceableLoader = new CompositeSequenceableLoader(trackStreams); } + // SampleSourceProvider implementation. + + @Override + public int getSourceCount() { + return 1; + } + + @Override + public SampleSource createSource(int index) { + Assertions.checkArgument(index == 0); + return this; + } + + // SampleSource implementation. + @Override public void prepare(Callback callback, Allocator allocator, long positionUs) { this.callback = callback; this.allocator = allocator; + trackStreams = newTrackStreamArray(0); + sequenceableLoader = new CompositeSequenceableLoader(trackStreams); + dataSource = dataSourceFactory.createDataSource(); + loader = new Loader("Loader:DashSampleSource"); manifestRefreshHandler = new Handler(); startLoadingManifest(); } @@ -212,14 +230,32 @@ public final class DashSampleSource implements SampleSource, @Override public void release() { + dataSource = null; + if (loader != null) { + loader.release(); + loader = null; + } + if (trackStreams != null) { + for (ChunkTrackStream trackStream : trackStreams) { + trackStream.release(); + } + trackStreams = null; + } + sequenceableLoader = null; + manifestLoadStartTimestamp = 0; + manifestLoadEndTimestamp = 0; + manifest = null; + callback = null; + allocator = null; if (manifestRefreshHandler != null) { manifestRefreshHandler.removeCallbacksAndMessages(null); manifestRefreshHandler = null; } - loader.release(); - for (ChunkTrackStream trackStream : trackStreams) { - trackStream.release(); - } + prepared = false; + durationUs = 0; + elapsedRealtimeOffset = 0; + trackGroups = null; + trackGroupAdaptationSetIndices = null; } // SequenceableLoader.Callback implementation. diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java index 7bc83b6729..04c57cffeb 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java @@ -21,6 +21,7 @@ import com.google.android.exoplayer.Format; import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.SampleSource; +import com.google.android.exoplayer.SampleSourceProvider; import com.google.android.exoplayer.SequenceableLoader; import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroupArray; @@ -47,7 +48,8 @@ import java.util.Arrays; import java.util.List; /** - * A {@link SampleSource} that extracts sample data using an {@link Extractor}. + * A {@link SampleSource} that extracts sample data using an {@link Extractor}. Also acts as a + * {@link SampleSourceProvider} providing {@link ExtractorSampleSource} instances. * *

If the possible input stream container formats are known, pass a factory that instantiates * extractors for them to the constructor. Otherwise, pass a {@link DefaultExtractorsFactory} to @@ -57,8 +59,8 @@ import java.util.List; * *

Note that the built-in extractors for AAC, MPEG TS and FLV streams do not support seeking. */ -public final class ExtractorSampleSource implements SampleSource, ExtractorOutput, - Loader.Callback, +public final class ExtractorSampleSource implements SampleSource, SampleSourceProvider, + ExtractorOutput, Loader.Callback, UpstreamFormatChangedListener { /** @@ -106,13 +108,17 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu private static final long DEFAULT_LAST_SAMPLE_DURATION_US = 10000; private final Uri uri; + private final DataSourceFactory dataSourceFactory; + private final BandwidthMeter bandwidthMeter; + private final ExtractorsFactory extractorsFactory; private final int minLoadableRetryCount; private final Handler eventHandler; private final EventListener eventListener; - private final DataSource dataSource; - private final ConditionVariable loadCondition; - private final ExtractorHolder extractorHolder; - private final Loader loader; + + private DataSource dataSource; + private ExtractorHolder extractorHolder; + private Loader loader; + private ConditionVariable loadCondition; private Callback callback; private Allocator allocator; @@ -166,16 +172,25 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu BandwidthMeter bandwidthMeter, ExtractorsFactory extractorsFactory, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener) { this.uri = uri; + this.dataSourceFactory = dataSourceFactory; + this.bandwidthMeter = bandwidthMeter; + this.extractorsFactory = extractorsFactory; this.minLoadableRetryCount = minLoadableRetryCount; - this.eventListener = eventListener; this.eventHandler = eventHandler; - dataSource = dataSourceFactory.createDataSource(bandwidthMeter); - loadCondition = new ConditionVariable(); - extractorHolder = new ExtractorHolder(extractorsFactory.createExtractors(), this); - loader = new Loader("Loader:ExtractorSampleSource", extractorHolder); - pendingResetPositionUs = C.UNSET_TIME_US; - sampleQueues = new DefaultTrackOutput[0]; - length = C.LENGTH_UNBOUNDED; + this.eventListener = eventListener; + } + + // SampleSourceProvider implementation. + + @Override + public int getSourceCount() { + return 1; + } + + @Override + public SampleSource createSource(int index) { + Assertions.checkArgument(index == 0); + return this; } // SampleSource implementation. @@ -184,6 +199,15 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu public void prepare(Callback callback, Allocator allocator, long positionUs) { this.callback = callback; this.allocator = allocator; + + dataSource = dataSourceFactory.createDataSource(bandwidthMeter); + extractorHolder = new ExtractorHolder(extractorsFactory.createExtractors(), this); + loader = new Loader("Loader:ExtractorSampleSource", extractorHolder); + loadCondition = new ConditionVariable(); + pendingResetPositionUs = C.UNSET_TIME_US; + sampleQueues = new DefaultTrackOutput[0]; + length = C.LENGTH_UNBOUNDED; + loadCondition.open(); startLoading(); } @@ -318,10 +342,35 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu @Override public void release() { - for (DefaultTrackOutput sampleQueue : sampleQueues) { - sampleQueue.disable(); + dataSource = null; + extractorHolder = null; + if (loader != null) { + loader.release(); // Releases extractorHolder via its own reference on the loader's thread. + loader = null; } - loader.release(); + loadCondition = null; + callback = null; + allocator = null; + seekMap = null; + tracksBuilt = false; + prepared = false; + seenFirstTrackSelection = false; + notifyReset = false; + enabledTrackCount = 0; + if (sampleQueues != null) { + for (DefaultTrackOutput sampleQueue : sampleQueues) { + sampleQueue.disable(); + } + sampleQueues = null; + } + tracks = null; + durationUs = 0; + trackEnabledStates = null; + length = 0; + lastSeekPositionUs = 0; + pendingResetPositionUs = 0; + extractedSamplesCountAtStartOfLoad = 0; + loadingFinished = false; } // TrackStream methods. diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java index 5d99dcd4c2..c65f85fc94 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer.CompositeSequenceableLoader; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.SampleSource; +import com.google.android.exoplayer.SampleSourceProvider; import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroupArray; import com.google.android.exoplayer.TrackSelection; @@ -38,6 +39,7 @@ import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.ParsingLoadable; +import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.MimeTypes; import android.net.Uri; @@ -51,9 +53,10 @@ import java.util.IdentityHashMap; import java.util.List; /** - * A {@link SampleSource} for HLS streams. + * A {@link SampleSource} for HLS streams. Also acts as a {@link SampleSourceProvider} providing + * {@link HlsSampleSource} instances. */ -public final class HlsSampleSource implements SampleSource, +public final class HlsSampleSource implements SampleSource, SampleSourceProvider, Loader.Callback>, HlsTrackStreamWrapper.Callback { /** @@ -68,10 +71,11 @@ public final class HlsSampleSource implements SampleSource, private final EventDispatcher eventDispatcher; private final IdentityHashMap trackStreamSources; private final PtsTimestampAdjusterProvider timestampAdjusterProvider; - private final Loader manifestFetcher; - private final DataSource manifestDataSource; private final HlsPlaylistParser manifestParser; + private DataSource manifestDataSource; + private Loader manifestFetcher; + private Callback callback; private Allocator allocator; private long preparePositionUs; @@ -100,25 +104,38 @@ public final class HlsSampleSource implements SampleSource, this.dataSourceFactory = dataSourceFactory; this.bandwidthMeter = bandwidthMeter; this.minLoadableRetryCount = minLoadableRetryCount; - eventDispatcher = new EventDispatcher(eventHandler, eventListener); - timestampAdjusterProvider = new PtsTimestampAdjusterProvider(); - trackStreamSources = new IdentityHashMap<>(); - manifestDataSource = dataSourceFactory.createDataSource(); + trackStreamSources = new IdentityHashMap<>(); + timestampAdjusterProvider = new PtsTimestampAdjusterProvider(); manifestParser = new HlsPlaylistParser(); - manifestFetcher = new Loader("Loader:ManifestFetcher"); } + // SampleSourceProvider implementation. + + @Override + public int getSourceCount() { + return 1; + } + + @Override + public SampleSource createSource(int index) { + Assertions.checkArgument(index == 0); + return this; + } + + // SampleSource implementation. + @Override public void prepare(Callback callback, Allocator allocator, long positionUs) { this.callback = callback; this.allocator = allocator; - this.preparePositionUs = positionUs; - ParsingLoadable loadable = new ParsingLoadable<>(manifestDataSource, - manifestUri, C.DATA_TYPE_MANIFEST, manifestParser); - long elapsedRealtimeMs = manifestFetcher.startLoading(loadable, this, - minLoadableRetryCount); + preparePositionUs = positionUs; + manifestDataSource = dataSourceFactory.createDataSource(); + manifestFetcher = new Loader("Loader:ManifestFetcher"); + ParsingLoadable loadable = new ParsingLoadable<>(manifestDataSource, manifestUri, + C.DATA_TYPE_MANIFEST, manifestParser); + long elapsedRealtimeMs = manifestFetcher.startLoading(loadable, this, minLoadableRetryCount); eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, elapsedRealtimeMs); } @@ -212,12 +229,30 @@ public final class HlsSampleSource implements SampleSource, @Override public void release() { - manifestFetcher.release(); + trackStreamSources.clear(); + timestampAdjusterProvider.reset(); + manifestDataSource = null; + if (manifestFetcher != null) { + manifestFetcher.release(); + manifestFetcher = null; + } + callback = null; + allocator = null; + preparePositionUs = 0; + pendingPrepareCount = 0; + seenFirstTrackSelection = false; + durationUs = 0; + isLive = false; + trackGroups = null; + selectedTrackCounts = null; if (trackStreamWrappers != null) { for (HlsTrackStreamWrapper trackStreamWrapper : trackStreamWrappers) { trackStreamWrapper.release(); } + trackStreamWrappers = null; } + enabledTrackStreamWrappers = null; + sequenceableLoader = null; } // Loader.Callback implementation. diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingSampleSource.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingSampleSource.java index e59b0bd3d1..543fa81aac 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingSampleSource.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer.CompositeSequenceableLoader; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.SampleSource; +import com.google.android.exoplayer.SampleSourceProvider; import com.google.android.exoplayer.SequenceableLoader; import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroupArray; @@ -39,6 +40,7 @@ import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.ParsingLoadable; +import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Util; import android.net.Uri; @@ -51,9 +53,10 @@ import java.util.Arrays; import java.util.List; /** - * A {@link SampleSource} for SmoothStreaming media. + * A {@link SampleSource} for SmoothStreaming media. Also acts as a {@link SampleSourceProvider} + * providing {@link SmoothStreamingSampleSource} instances. */ -public final class SmoothStreamingSampleSource implements SampleSource, +public final class SmoothStreamingSampleSource implements SampleSource, SampleSourceProvider, SequenceableLoader.Callback>, Loader.Callback> { @@ -70,10 +73,13 @@ public final class SmoothStreamingSampleSource implements SampleSource, private final BandwidthMeter bandwidthMeter; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; - private final Loader manifestLoader; - private final DataSource manifestDataSource; private final SmoothStreamingManifestParser manifestParser; + private DataSource manifestDataSource; + private Loader manifestLoader; + private ChunkTrackStream[] trackStreams; + private CompositeSequenceableLoader sequenceableLoader; + private long manifestLoadStartTimestamp; private SmoothStreamingManifest manifest; @@ -86,9 +92,6 @@ public final class SmoothStreamingSampleSource implements SampleSource, private TrackGroupArray trackGroups; private int[] trackGroupElementIndices; - private ChunkTrackStream[] trackStreams; - private CompositeSequenceableLoader sequenceableLoader; - public SmoothStreamingSampleSource(Uri manifestUri, DataSourceFactory dataSourceFactory, BandwidthMeter bandwidthMeter, Handler eventHandler, AdaptiveSourceEventListener eventListener) { @@ -105,17 +108,32 @@ public final class SmoothStreamingSampleSource implements SampleSource, this.bandwidthMeter = bandwidthMeter; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = new EventDispatcher(eventHandler, eventListener); - trackStreams = newTrackStreamArray(0); - sequenceableLoader = new CompositeSequenceableLoader(trackStreams); - manifestDataSource = dataSourceFactory.createDataSource(); manifestParser = new SmoothStreamingManifestParser(); - manifestLoader = new Loader("Loader:Manifest"); } + // SampleSourceProvider implementation. + + @Override + public int getSourceCount() { + return 1; + } + + @Override + public SampleSource createSource(int index) { + Assertions.checkArgument(index == 0); + return this; + } + + // SampleSource implementation. + @Override public void prepare(Callback callback, Allocator allocator, long positionUs) { this.callback = callback; this.allocator = allocator; + trackStreams = newTrackStreamArray(0); + sequenceableLoader = new CompositeSequenceableLoader(trackStreams); + manifestDataSource = dataSourceFactory.createDataSource(); + manifestLoader = new Loader("Loader:Manifest"); manifestRefreshHandler = new Handler(); startLoadingManifest(); } @@ -202,14 +220,31 @@ public final class SmoothStreamingSampleSource implements SampleSource, @Override public void release() { + manifestDataSource = null; + if (manifestLoader != null) { + manifestLoader.release(); + manifestLoader = null; + } + if (trackStreams != null) { + for (ChunkTrackStream trackStream : trackStreams) { + trackStream.release(); + } + trackStreams = null; + } + sequenceableLoader = null; + manifestLoadStartTimestamp = 0; + manifest = null; + callback = null; + allocator = null; if (manifestRefreshHandler != null) { manifestRefreshHandler.removeCallbacksAndMessages(null); manifestRefreshHandler = null; } - manifestLoader.release(); - for (ChunkTrackStream trackStream : trackStreams) { - trackStream.release(); - } + prepared = false; + durationUs = 0; + trackEncryptionBoxes = null; + trackGroups = null; + trackGroupElementIndices = null; } // SequenceableLoader.Callback implementation diff --git a/playbacktests/src/main/java/com/google/android/exoplayer/playbacktests/gts/DashTest.java b/playbacktests/src/main/java/com/google/android/exoplayer/playbacktests/gts/DashTest.java index e9d677c8dc..c1d39426d7 100644 --- a/playbacktests/src/main/java/com/google/android/exoplayer/playbacktests/gts/DashTest.java +++ b/playbacktests/src/main/java/com/google/android/exoplayer/playbacktests/gts/DashTest.java @@ -22,7 +22,7 @@ import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.MediaCodecUtil; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; -import com.google.android.exoplayer.SampleSource; +import com.google.android.exoplayer.SampleSourceProvider; import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroupArray; import com.google.android.exoplayer.TrackRenderer; @@ -44,6 +44,7 @@ import android.annotation.TargetApi; import android.net.Uri; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; + import junit.framework.AssertionFailedError; import java.util.ArrayList; @@ -418,7 +419,7 @@ public final class DashTest extends ActivityInstrumentationTestCase2