Make SampleSources reusable and implement SampleSourceProvider.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=126888556
This commit is contained in:
andrewlewis 2016-07-08 00:57:45 -07:00 committed by Oliver Woodman
parent 42d78b3559
commit af3452d231
15 changed files with 354 additions and 273 deletions

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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<SingleSampleSource>, Loadable {
public final class SingleSampleSource implements SampleSource, SampleSourceProvider, TrackStream,
Loader.Callback<SingleSampleSource.SourceLoadable> {
/**
* 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();
}
}
}
}

View File

@ -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<ChunkTrackStream<DashChunkSource>> {
/**
@ -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<DashChunkSource>[] 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<DashChunkSource>[] 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<DashChunkSource> 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<DashChunkSource> trackStream : trackStreams) {
trackStream.release();
}
prepared = false;
durationUs = 0;
elapsedRealtimeOffset = 0;
trackGroups = null;
trackGroupAdaptationSetIndices = null;
}
// SequenceableLoader.Callback implementation.

View File

@ -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.
*
* <p>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;
*
* <p>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<ExtractorSampleSource.ExtractingLoadable>,
public final class ExtractorSampleSource implements SampleSource, SampleSourceProvider,
ExtractorOutput, Loader.Callback<ExtractorSampleSource.ExtractingLoadable>,
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.

View File

@ -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<ParsingLoadable<HlsPlaylist>>, HlsTrackStreamWrapper.Callback {
/**
@ -68,10 +71,11 @@ public final class HlsSampleSource implements SampleSource,
private final EventDispatcher eventDispatcher;
private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> 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<HlsPlaylist> 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<HlsPlaylist> 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.

View File

@ -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<ChunkTrackStream<SmoothStreamingChunkSource>>,
Loader.Callback<ParsingLoadable<SmoothStreamingManifest>> {
@ -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<SmoothStreamingChunkSource>[] 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<SmoothStreamingChunkSource>[] 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<SmoothStreamingChunkSource> 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<SmoothStreamingChunkSource> trackStream : trackStreams) {
trackStream.release();
}
prepared = false;
durationUs = 0;
trackEncryptionBoxes = null;
trackGroups = null;
trackGroupElementIndices = null;
}
// SequenceableLoader.Callback implementation

View File

@ -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<HostActivit
}
@Override
public SampleSource buildSource(HostActivity host, DataSourceFactory dataSourceFactory,
public SampleSourceProvider buildSource(HostActivity host, DataSourceFactory dataSourceFactory,
BandwidthMeter bandwidthMeter) {
return new DashSampleSource(manifestUri, dataSourceFactory, bandwidthMeter,
MIN_LOADABLE_RETRY_COUNT, null, null);

View File

@ -22,7 +22,7 @@ import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.ExoPlayerFactory;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SampleSourceProvider;
import com.google.android.exoplayer.SimpleExoPlayer;
import com.google.android.exoplayer.TrackSelectionPolicy;
import com.google.android.exoplayer.audio.AudioTrack;
@ -129,7 +129,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
player = buildExoPlayer(host, surface, trackSelector);
DataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(host, Util
.getUserAgent(host, "ExoPlayerPlaybackTests"));
player.setSource(buildSource(host, dataSourceFactory, player.getBandwidthMeter()));
player.setSourceProvider(buildSource(host, dataSourceFactory, player.getBandwidthMeter()));
player.addListener(this);
player.setDebugListener(this);
player.setPlayWhenReady(true);
@ -288,7 +288,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
}
@SuppressWarnings("unused")
protected abstract SampleSource buildSource(HostActivity host,
protected abstract SampleSourceProvider buildSource(HostActivity host,
DataSourceFactory dataSourceFactory, BandwidthMeter bandwidthMeter);
@SuppressWarnings("unused")