Add DataSourceFactory + implementation.

Step 6 of the refactor involves moving the logic that's
currently in the SourceBuilder classes in the demo app
into new SampleSource classes in the library. These classes
will construct video/audio/text pipelines on-demand (i.e.
when tracks are enabled) rather than constructing them all
up front as is currently the case in the SourceBuilder
classes. Hence we need a way to instantiate DataSource
instances (i.e. a DataSourceFactory ;)).
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=118722180
This commit is contained in:
olly 2016-03-31 14:58:32 -07:00 committed by Oliver Woodman
parent 5f37e3c8fb
commit b0c8a6132d
8 changed files with 142 additions and 89 deletions

View file

@ -37,6 +37,8 @@ import com.google.android.exoplayer.metadata.id3.TxxxFrame;
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.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer.util.DebugTextViewHelper;
import com.google.android.exoplayer.util.Util;
@ -105,6 +107,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
private Button textButton;
private Button retryButton;
private DataSourceFactory dataSourceFactory;
private DemoPlayer player;
private DefaultTrackSelector trackSelector;
private TrackSelectionHelper trackSelectionHelper;
@ -123,6 +126,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String userAgent = Util.getUserAgent(this, "ExoPlayerDemo");
dataSourceFactory = new DefaultDataSourceFactory(this, userAgent);
setContentView(R.layout.player_activity);
View root = findViewById(R.id.root);
@ -285,15 +290,15 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
String userAgent = Util.getUserAgent(this, "ExoPlayerDemo");
switch (contentType) {
case Util.TYPE_SS:
return new SmoothStreamingSourceBuilder(this, userAgent, contentUri.toString(),
return new SmoothStreamingSourceBuilder(dataSourceFactory, contentUri.toString(),
new SmoothStreamingTestMediaDrmCallback());
case Util.TYPE_DASH:
return new DashSourceBuilder(this, userAgent, contentUri.toString(),
return new DashSourceBuilder(dataSourceFactory, contentUri.toString(),
new WidevineTestMediaDrmCallback(contentId, provider));
case Util.TYPE_HLS:
return new HlsSourceBuilder(this, userAgent, contentUri.toString());
return new HlsSourceBuilder(dataSourceFactory, contentUri.toString());
case Util.TYPE_OTHER:
return new ExtractorSourceBuilder(this, userAgent, contentUri);
return new ExtractorSourceBuilder(dataSourceFactory, contentUri);
default:
throw new IllegalStateException("Unsupported type: " + contentType);
}

View file

@ -30,11 +30,10 @@ import com.google.android.exoplayer.demo.player.DemoPlayer.SourceBuilder;
import com.google.android.exoplayer.drm.MediaDrmCallback;
import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultDataSource;
import com.google.android.exoplayer.util.ManifestFetcher;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
@ -53,15 +52,13 @@ public class DashSourceBuilder implements SourceBuilder {
private static final int TEXT_BUFFER_SEGMENTS = 2;
private static final int LIVE_EDGE_LATENCY_MS = 30000;
private final Context context;
private final String userAgent;
private final DataSourceFactory dataSourceFactory;
private final String url;
private final MediaDrmCallback drmCallback;
public DashSourceBuilder(Context context, String userAgent, String url,
public DashSourceBuilder(DataSourceFactory dataSourceFactory, String url,
MediaDrmCallback drmCallback) {
this.context = context;
this.userAgent = userAgent;
this.dataSourceFactory = dataSourceFactory;
this.url = url;
this.drmCallback = drmCallback;
}
@ -69,7 +66,7 @@ public class DashSourceBuilder implements SourceBuilder {
@Override
public SampleSource buildRenderers(DemoPlayer player) {
MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser();
DefaultDataSource manifestDataSource = new DefaultDataSource(context, userAgent);
DataSource manifestDataSource = dataSourceFactory.createDataSource();
ManifestFetcher<MediaPresentationDescription> manifestFetcher = new ManifestFetcher<>(
Uri.parse(url), manifestDataSource, parser);
@ -78,7 +75,7 @@ public class DashSourceBuilder implements SourceBuilder {
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
// Build the video renderer.
DataSource videoDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource videoDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, AdaptationSet.TYPE_VIDEO,
videoDataSource, new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS,
0, mainHandler, player, DemoPlayer.TYPE_VIDEO);
@ -86,7 +83,7 @@ public class DashSourceBuilder implements SourceBuilder {
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_VIDEO);
// Build the audio renderer.
DataSource audioDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
ChunkSource audioChunkSource = new DashChunkSource(manifestFetcher, AdaptationSet.TYPE_AUDIO,
audioDataSource, null, LIVE_EDGE_LATENCY_MS, 0, mainHandler, player, DemoPlayer.TYPE_AUDIO);
ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl,
@ -94,7 +91,7 @@ public class DashSourceBuilder implements SourceBuilder {
DemoPlayer.TYPE_AUDIO);
// Build the text renderer.
DataSource textDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource textDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
ChunkSource textChunkSource = new DashChunkSource(manifestFetcher, AdaptationSet.TYPE_TEXT,
textDataSource, null, LIVE_EDGE_LATENCY_MS, 0, mainHandler, player, DemoPlayer.TYPE_TEXT);
ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl,

View file

@ -21,10 +21,9 @@ import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
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.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultDataSource;
import android.content.Context;
import android.net.Uri;
/**
@ -35,20 +34,18 @@ public class ExtractorSourceBuilder implements SourceBuilder {
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 256;
private final Context context;
private final String userAgent;
private final DataSourceFactory dataSourceFactory;
private final Uri uri;
public ExtractorSourceBuilder(Context context, String userAgent, Uri uri) {
this.context = context;
this.userAgent = userAgent;
public ExtractorSourceBuilder(DataSourceFactory dataSourceFactory, Uri uri) {
this.dataSourceFactory = dataSourceFactory;
this.uri = uri;
}
@Override
public SampleSource buildRenderers(DemoPlayer player) {
Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
DataSource dataSource = new DefaultDataSource(context, player.getBandwidthMeter(), userAgent);
DataSource dataSource = dataSourceFactory.createDataSource(player.getBandwidthMeter());
return new ExtractorSampleSource(uri, dataSource, allocator,
BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE, player.getMainHandler(), player, 0);
}

View file

@ -28,11 +28,10 @@ import com.google.android.exoplayer.hls.HlsSampleSource;
import com.google.android.exoplayer.hls.PtsTimestampAdjusterProvider;
import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultDataSource;
import com.google.android.exoplayer.util.ManifestFetcher;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
@ -47,20 +46,18 @@ public class HlsSourceBuilder implements SourceBuilder {
private static final int AUDIO_BUFFER_SEGMENTS = 54;
private static final int TEXT_BUFFER_SEGMENTS = 2;
private final Context context;
private final String userAgent;
private final DataSourceFactory dataSourceFactory;
private final String url;
public HlsSourceBuilder(Context context, String userAgent, String url) {
this.context = context;
this.userAgent = userAgent;
public HlsSourceBuilder(DataSourceFactory dataSourceFactory, String url) {
this.dataSourceFactory = dataSourceFactory;
this.url = url;
}
@Override
public SampleSource buildRenderers(DemoPlayer player) {
HlsPlaylistParser parser = new HlsPlaylistParser();
DefaultDataSource manifestDataSource = new DefaultDataSource(context, userAgent);
DataSource manifestDataSource = dataSourceFactory.createDataSource();
ManifestFetcher<HlsPlaylist> manifestFetcher = new ManifestFetcher<>(Uri.parse(url),
manifestDataSource, parser);
@ -69,20 +66,20 @@ public class HlsSourceBuilder implements SourceBuilder {
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
DataSource defaultDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource defaultDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource defaultChunkSource = new HlsChunkSource(manifestFetcher,
HlsChunkSource.TYPE_DEFAULT, defaultDataSource, timestampAdjusterProvider,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter));
HlsSampleSource defaultSampleSource = new HlsSampleSource(defaultChunkSource, loadControl,
MAIN_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_VIDEO);
DataSource audioDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource audioChunkSource = new HlsChunkSource(manifestFetcher, HlsChunkSource.TYPE_AUDIO,
audioDataSource, timestampAdjusterProvider, null);
HlsSampleSource audioSampleSource = new HlsSampleSource(audioChunkSource, loadControl,
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_AUDIO);
DataSource subtitleDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource subtitleDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource subtitleChunkSource = new HlsChunkSource(manifestFetcher,
HlsChunkSource.TYPE_SUBTITLE, subtitleDataSource, timestampAdjusterProvider, null);
HlsSampleSource subtitleSampleSource = new HlsSampleSource(subtitleChunkSource, loadControl,

View file

@ -29,13 +29,10 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifestParser;
import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultDataSource;
import com.google.android.exoplayer.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.Util;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
@ -51,30 +48,29 @@ public class SmoothStreamingSourceBuilder implements SourceBuilder {
private static final int TEXT_BUFFER_SEGMENTS = 2;
private static final int LIVE_EDGE_LATENCY_MS = 30000;
private final Context context;
private final String userAgent;
private final DataSourceFactory dataSourceFactory;
private final String url;
private final MediaDrmCallback drmCallback;
public SmoothStreamingSourceBuilder(Context context, String userAgent, String url,
public SmoothStreamingSourceBuilder(DataSourceFactory dataSourceFactory, String url,
MediaDrmCallback drmCallback) {
this.context = context;
this.userAgent = userAgent;
this.url = Util.toLowerInvariant(url).endsWith("/manifest") ? url : url + "/Manifest";
this.dataSourceFactory = dataSourceFactory;
this.url = url;
this.drmCallback = drmCallback;
}
@Override
public SampleSource buildRenderers(DemoPlayer player) {
SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser();
DataSource manifestDataSource = dataSourceFactory.createDataSource();
ManifestFetcher<SmoothStreamingManifest> manifestFetcher = new ManifestFetcher<>(Uri.parse(url),
new DefaultHttpDataSource(userAgent, null), parser);
manifestDataSource, parser);
Handler mainHandler = player.getMainHandler();
BandwidthMeter bandwidthMeter = player.getBandwidthMeter();
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
// Build the video renderer.
DataSource videoDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource videoDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher,
SmoothStreamingManifest.StreamElement.TYPE_VIDEO,
videoDataSource, new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS);
@ -83,7 +79,7 @@ public class SmoothStreamingSourceBuilder implements SourceBuilder {
DemoPlayer.TYPE_VIDEO);
// Build the audio renderer.
DataSource audioDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
ChunkSource audioChunkSource = new SmoothStreamingChunkSource(manifestFetcher,
SmoothStreamingManifest.StreamElement.TYPE_AUDIO, audioDataSource, null,
LIVE_EDGE_LATENCY_MS);
@ -91,7 +87,7 @@ public class SmoothStreamingSourceBuilder implements SourceBuilder {
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_AUDIO);
// Build the text renderer.
DataSource textDataSource = new DefaultDataSource(context, bandwidthMeter, userAgent);
DataSource textDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
ChunkSource textChunkSource = new SmoothStreamingChunkSource(manifestFetcher,
SmoothStreamingManifest.StreamElement.TYPE_TEXT, textDataSource, null,
LIVE_EDGE_LATENCY_MS);

View file

@ -0,0 +1,35 @@
/*
* 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.upstream;
/**
* A factory for {@link DataSource} instances.
*/
public interface DataSourceFactory {
/**
* Creates a {@link DataSource} instance.
*/
DataSource createDataSource();
/**
* Creates a {@link DataSource} instance.
*
* @param listener A {@link TransferListener} to receive events from the instance.
*/
DataSource createDataSource(TransferListener listener);
}

View file

@ -40,48 +40,13 @@ public final class DefaultDataSource implements DataSource {
private static final String SCHEME_ASSET = "asset";
private static final String SCHEME_CONTENT = "content";
private final DataSource httpDataSource;
private final DataSource defaultDataSource;
private final DataSource fileDataSource;
private final DataSource assetDataSource;
private final DataSource contentDataSource;
/**
* {@code null} if no data source is open. Otherwise, equal to {@link #fileDataSource} if the open
* data source is a file, or {@link #httpDataSource} otherwise.
*/
private DataSource dataSource;
/**
* Constructs a new instance.
* <p>
* The constructed instance will not follow cross-protocol redirects (i.e. redirects from HTTP to
* HTTPS or vice versa) when fetching remote data. Cross-protocol redirects can be enabled by
* using {@link #DefaultDataSource(Context, TransferListener, String, boolean)} and passing
* {@code true} as the final argument.
*
* @param context A context.
* @param userAgent The User-Agent string that should be used when requesting remote data.
*/
public DefaultDataSource(Context context, String userAgent) {
this(context, null, userAgent, false);
}
/**
* Constructs a new instance.
* <p>
* The constructed instance will not follow cross-protocol redirects (i.e. redirects from HTTP to
* HTTPS or vice versa) when fetching remote data. Cross-protocol redirects can be enabled by
* using {@link #DefaultDataSource(Context, TransferListener, String, boolean)} and passing
* {@code true} as the final argument.
*
* @param context A context.
* @param listener An optional {@link TransferListener}.
* @param userAgent The User-Agent string that should be used when requesting remote data.
*/
public DefaultDataSource(Context context, TransferListener listener, String userAgent) {
this(context, listener, userAgent, false);
}
/**
* Constructs a new instance, optionally configured to follow cross-protocol redirects.
*
@ -100,15 +65,16 @@ public final class DefaultDataSource implements DataSource {
}
/**
* Constructs a new instance, using a provided {@link HttpDataSource} for fetching remote data.
* Constructs a new instance, using a provided {@link DataSource} for fetching remote data.
*
* @param context A context.
* @param listener An optional {@link TransferListener}.
* @param httpDataSource {@link DataSource} to use for non-file URIs.
* @param defaultDataSource A {@link DataSource} to use for all URIs other than file, asset and
* content URIs. This {@link DataSource} should normally handle at least http(s) URIs.
*/
public DefaultDataSource(Context context, TransferListener listener,
DataSource httpDataSource) {
this.httpDataSource = Assertions.checkNotNull(httpDataSource);
DataSource defaultDataSource) {
this.defaultDataSource = Assertions.checkNotNull(defaultDataSource);
this.fileDataSource = new FileDataSource(listener);
this.assetDataSource = new AssetDataSource(context, listener);
this.contentDataSource = new ContentDataSource(context, listener);
@ -130,7 +96,7 @@ public final class DefaultDataSource implements DataSource {
} else if (SCHEME_CONTENT.equals(scheme)) {
dataSource = contentDataSource;
} else {
dataSource = httpDataSource;
dataSource = defaultDataSource;
}
// Open the source and return.
return dataSource.open(dataSpec);

View file

@ -0,0 +1,60 @@
/*
* 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.upstream;
import android.content.Context;
/**
* A {@link DataSourceFactory} that produces {@link DefaultDataSource} instances.
*/
public final class DefaultDataSourceFactory implements DataSourceFactory {
private final Context context;
private final String userAgent;
private final boolean allowCrossProtocolRedirects;
private final DataSource httpDataSource;
public DefaultDataSourceFactory(Context context, String userAgent) {
this(context, userAgent, false);
}
public DefaultDataSourceFactory(Context context, String userAgent,
boolean allowCrossProtocolRedirects) {
this.context = context.getApplicationContext();
this.userAgent = userAgent;
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
this.httpDataSource = null;
}
public DefaultDataSourceFactory(Context context, DataSource httpDataSource) {
this.context = context.getApplicationContext();
this.httpDataSource = httpDataSource;
this.userAgent = null;
this.allowCrossProtocolRedirects = false;
}
@Override
public DataSource createDataSource() {
return createDataSource(null);
}
@Override
public DataSource createDataSource(TransferListener listener) {
return httpDataSource != null ? new DefaultDataSource(context, listener, httpDataSource)
: new DefaultDataSource(context, listener, userAgent, allowCrossProtocolRedirects);
}
}