Commit 675c7738 by Oliver Woodman

Let DefaultUriDataSource load assets.

parent f474afbf
...@@ -233,19 +233,19 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -233,19 +233,19 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
audioCapabilities); audioCapabilities);
case DemoUtil.TYPE_M4A: // There are no file format differences between M4A and MP4. case DemoUtil.TYPE_M4A: // There are no file format differences between M4A and MP4.
case DemoUtil.TYPE_MP4: case DemoUtil.TYPE_MP4:
return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView, return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView,
new Mp4Extractor()); new Mp4Extractor());
case DemoUtil.TYPE_MP3: case DemoUtil.TYPE_MP3:
return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView, return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView,
new Mp3Extractor()); new Mp3Extractor());
case DemoUtil.TYPE_TS: case DemoUtil.TYPE_TS:
return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView, return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView,
new TsExtractor(0, audioCapabilities)); new TsExtractor(0, audioCapabilities));
case DemoUtil.TYPE_AAC: case DemoUtil.TYPE_AAC:
return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView, return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView,
new AdtsExtractor()); new AdtsExtractor());
case DemoUtil.TYPE_WEBM: case DemoUtil.TYPE_WEBM:
return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView, return new ExtractorRendererBuilder(this, userAgent, contentUri, debugTextView,
new WebmExtractor()); new WebmExtractor());
default: default:
throw new IllegalStateException("Unsupported type: " + contentType); throw new IllegalStateException("Unsupported type: " + contentType);
......
...@@ -130,7 +130,7 @@ public class DashRendererBuilder implements RendererBuilder, ...@@ -130,7 +130,7 @@ public class DashRendererBuilder implements RendererBuilder,
this.player = player; this.player = player;
this.callback = callback; this.callback = callback;
MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser(); MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser();
manifestDataSource = new DefaultUriDataSource(userAgent, null); manifestDataSource = new DefaultUriDataSource(context, userAgent);
manifestFetcher = new ManifestFetcher<MediaPresentationDescription>(url, manifestDataSource, manifestFetcher = new ManifestFetcher<MediaPresentationDescription>(url, manifestDataSource,
parser); parser);
manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this); manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this);
...@@ -232,10 +232,10 @@ public class DashRendererBuilder implements RendererBuilder, ...@@ -232,10 +232,10 @@ public class DashRendererBuilder implements RendererBuilder,
videoRenderer = null; videoRenderer = null;
debugRenderer = null; debugRenderer = null;
} else { } else {
DataSource videoDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter); DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, videoAdaptationSetIndex, ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher,
videoRepresentationIndices, videoDataSource, new AdaptiveEvaluator(bandwidthMeter), videoAdaptationSetIndex, videoRepresentationIndices, videoDataSource,
LIVE_EDGE_LATENCY_MS, elapsedRealtimeOffset); new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS, elapsedRealtimeOffset);
ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl,
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true, mainHandler, player, VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true, mainHandler, player,
DemoPlayer.TYPE_VIDEO); DemoPlayer.TYPE_VIDEO);
...@@ -249,7 +249,7 @@ public class DashRendererBuilder implements RendererBuilder, ...@@ -249,7 +249,7 @@ public class DashRendererBuilder implements RendererBuilder,
List<ChunkSource> audioChunkSourceList = new ArrayList<ChunkSource>(); List<ChunkSource> audioChunkSourceList = new ArrayList<ChunkSource>();
List<String> audioTrackNameList = new ArrayList<String>(); List<String> audioTrackNameList = new ArrayList<String>();
if (audioAdaptationSet != null) { if (audioAdaptationSet != null) {
DataSource audioDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter); DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
FormatEvaluator audioEvaluator = new FormatEvaluator.FixedEvaluator(); FormatEvaluator audioEvaluator = new FormatEvaluator.FixedEvaluator();
List<Representation> audioRepresentations = audioAdaptationSet.representations; List<Representation> audioRepresentations = audioAdaptationSet.representations;
List<String> codecs = new ArrayList<String>(); List<String> codecs = new ArrayList<String>();
...@@ -304,7 +304,7 @@ public class DashRendererBuilder implements RendererBuilder, ...@@ -304,7 +304,7 @@ public class DashRendererBuilder implements RendererBuilder,
} }
// Build the text chunk sources. // Build the text chunk sources.
DataSource textDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter); DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
FormatEvaluator textEvaluator = new FormatEvaluator.FixedEvaluator(); FormatEvaluator textEvaluator = new FormatEvaluator.FixedEvaluator();
List<ChunkSource> textChunkSourceList = new ArrayList<ChunkSource>(); List<ChunkSource> textChunkSourceList = new ArrayList<ChunkSource>();
List<String> textTrackNameList = new ArrayList<String>(); List<String> textTrackNameList = new ArrayList<String>();
......
...@@ -25,6 +25,7 @@ import com.google.android.exoplayer.extractor.ExtractorSampleSource; ...@@ -25,6 +25,7 @@ import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DefaultUriDataSource; import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import android.content.Context;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.net.Uri; import android.net.Uri;
import android.widget.TextView; import android.widget.TextView;
...@@ -36,13 +37,15 @@ public class ExtractorRendererBuilder implements RendererBuilder { ...@@ -36,13 +37,15 @@ public class ExtractorRendererBuilder implements RendererBuilder {
private static final int BUFFER_SIZE = 10 * 1024 * 1024; private static final int BUFFER_SIZE = 10 * 1024 * 1024;
private final Context context;
private final String userAgent; private final String userAgent;
private final Uri uri; private final Uri uri;
private final TextView debugTextView; private final TextView debugTextView;
private final Extractor extractor; private final Extractor extractor;
public ExtractorRendererBuilder(String userAgent, Uri uri, TextView debugTextView, public ExtractorRendererBuilder(Context context, String userAgent, Uri uri,
Extractor extractor) { TextView debugTextView, Extractor extractor) {
this.context = context;
this.userAgent = userAgent; this.userAgent = userAgent;
this.uri = uri; this.uri = uri;
this.debugTextView = debugTextView; this.debugTextView = debugTextView;
...@@ -52,7 +55,7 @@ public class ExtractorRendererBuilder implements RendererBuilder { ...@@ -52,7 +55,7 @@ public class ExtractorRendererBuilder implements RendererBuilder {
@Override @Override
public void buildRenderers(DemoPlayer player, RendererBuilderCallback callback) { public void buildRenderers(DemoPlayer player, RendererBuilderCallback callback) {
// Build the video and audio renderers. // Build the video and audio renderers.
DataSource dataSource = new DefaultUriDataSource(userAgent, null); DataSource dataSource = new DefaultUriDataSource(context, userAgent);
ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, extractor, 2, ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, extractor, 2,
BUFFER_SIZE); BUFFER_SIZE);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource, MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource,
......
...@@ -76,8 +76,8 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls ...@@ -76,8 +76,8 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
this.player = player; this.player = player;
this.callback = callback; this.callback = callback;
HlsPlaylistParser parser = new HlsPlaylistParser(); HlsPlaylistParser parser = new HlsPlaylistParser();
ManifestFetcher<HlsPlaylist> playlistFetcher = ManifestFetcher<HlsPlaylist> playlistFetcher = new ManifestFetcher<HlsPlaylist>(url,
new ManifestFetcher<HlsPlaylist>(url, new DefaultUriDataSource(userAgent, null), parser); new DefaultUriDataSource(context, userAgent), parser);
playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this); playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this);
} }
...@@ -103,7 +103,7 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls ...@@ -103,7 +103,7 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback<Hls
} }
} }
DataSource dataSource = new DefaultUriDataSource(userAgent, bandwidthMeter); DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
HlsChunkSource chunkSource = new HlsChunkSource(dataSource, url, manifest, bandwidthMeter, HlsChunkSource chunkSource = new HlsChunkSource(dataSource, url, manifest, bandwidthMeter,
variantIndices, HlsChunkSource.ADAPTIVE_MODE_SPLICE, audioCapabilities); variantIndices, HlsChunkSource.ADAPTIVE_MODE_SPLICE, audioCapabilities);
HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, true, 3, REQUESTED_BUFFER_SIZE, HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, true, 3, REQUESTED_BUFFER_SIZE,
......
...@@ -160,7 +160,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, ...@@ -160,7 +160,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
videoRenderer = null; videoRenderer = null;
debugRenderer = null; debugRenderer = null;
} else { } else {
DataSource videoDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter); DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher, ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher,
videoStreamElementIndex, videoTrackIndices, videoDataSource, videoStreamElementIndex, videoTrackIndices, videoDataSource,
new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS); new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS);
...@@ -184,7 +184,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, ...@@ -184,7 +184,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
} else { } else {
audioTrackNames = new String[audioStreamElementCount]; audioTrackNames = new String[audioStreamElementCount];
ChunkSource[] audioChunkSources = new ChunkSource[audioStreamElementCount]; ChunkSource[] audioChunkSources = new ChunkSource[audioStreamElementCount];
DataSource audioDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter); DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
FormatEvaluator audioFormatEvaluator = new FormatEvaluator.FixedEvaluator(); FormatEvaluator audioFormatEvaluator = new FormatEvaluator.FixedEvaluator();
audioStreamElementCount = 0; audioStreamElementCount = 0;
for (int i = 0; i < manifest.streamElements.length; i++) { for (int i = 0; i < manifest.streamElements.length; i++) {
...@@ -215,7 +215,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder, ...@@ -215,7 +215,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
} else { } else {
textTrackNames = new String[textStreamElementCount]; textTrackNames = new String[textStreamElementCount];
ChunkSource[] textChunkSources = new ChunkSource[textStreamElementCount]; ChunkSource[] textChunkSources = new ChunkSource[textStreamElementCount];
DataSource ttmlDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter); DataSource ttmlDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
FormatEvaluator ttmlFormatEvaluator = new FormatEvaluator.FixedEvaluator(); FormatEvaluator ttmlFormatEvaluator = new FormatEvaluator.FixedEvaluator();
textStreamElementCount = 0; textStreamElementCount = 0;
for (int i = 0; i < manifest.streamElements.length; i++) { for (int i = 0; i < manifest.streamElements.length; i++) {
......
...@@ -18,6 +18,7 @@ package com.google.android.exoplayer.upstream; ...@@ -18,6 +18,7 @@ package com.google.android.exoplayer.upstream;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import android.content.Context;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import java.io.EOFException; import java.io.EOFException;
...@@ -32,7 +33,7 @@ public final class AssetDataSource implements UriDataSource { ...@@ -32,7 +33,7 @@ public final class AssetDataSource implements UriDataSource {
/** /**
* Thrown when IOException is encountered during local asset read operation. * Thrown when IOException is encountered during local asset read operation.
*/ */
public static class AssetDataSourceException extends IOException { public static final class AssetDataSourceException extends IOException {
public AssetDataSourceException(IOException cause) { public AssetDataSourceException(IOException cause) {
super(cause); super(cause);
...@@ -51,8 +52,8 @@ public final class AssetDataSource implements UriDataSource { ...@@ -51,8 +52,8 @@ public final class AssetDataSource implements UriDataSource {
/** /**
* Constructs a new {@link DataSource} that retrieves data from a local asset. * Constructs a new {@link DataSource} that retrieves data from a local asset.
*/ */
public AssetDataSource(AssetManager assetManager) { public AssetDataSource(Context context) {
this(assetManager, null); this(context, null);
} }
/** /**
...@@ -60,8 +61,8 @@ public final class AssetDataSource implements UriDataSource { ...@@ -60,8 +61,8 @@ public final class AssetDataSource implements UriDataSource {
* *
* @param listener An optional listener. Specify {@code null} for no listener. * @param listener An optional listener. Specify {@code null} for no listener.
*/ */
public AssetDataSource(AssetManager assetManager, TransferListener listener) { public AssetDataSource(Context context, TransferListener listener) {
this.assetManager = assetManager; this.assetManager = context.getAssets();
this.listener = listener; this.listener = listener;
} }
......
...@@ -17,17 +17,49 @@ package com.google.android.exoplayer.upstream; ...@@ -17,17 +17,49 @@ package com.google.android.exoplayer.upstream;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import android.content.Context;
import java.io.IOException; import java.io.IOException;
/** /**
* A data source that fetches data from a local or remote {@link DataSpec}. * A {@link UriDataSource} that supports multiple URI schemes. The supported schemes are:
*
* <ul>
* <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4).
* <li>file: For fetching data from a local file (e.g. file:///path/to/media/media.mp4).
* <li>asset: For fetching data from an asset in the application's apk (e.g. asset:///media.mp4).
* </ul>
*/ */
public final class DefaultUriDataSource implements UriDataSource { public final class DefaultUriDataSource implements UriDataSource {
private static final String FILE_URI_SCHEME = "file"; /**
* Thrown when a {@link DefaultUriDataSource} is opened for a URI with an unsupported scheme.
*/
public static final class UnsupportedSchemeException extends IOException {
/**
* The unsupported scheme.
*/
public final String scheme;
/**
* @param scheme The unsupported scheme.
*/
public UnsupportedSchemeException(String scheme) {
super("Unsupported URI scheme: " + scheme);
this.scheme = scheme;
}
}
private static final String SCHEME_HTTP = "http";
private static final String SCHEME_HTTPS = "https";
private static final String SCHEME_FILE = "file";
private static final String SCHEME_ASSET = "asset";
private final UriDataSource fileDataSource;
private final UriDataSource httpDataSource; private final UriDataSource httpDataSource;
private final UriDataSource fileDataSource;
private final UriDataSource assetDataSource;
/** /**
* {@code null} if no data source is open. Otherwise, equal to {@link #fileDataSource} if the open * {@code null} if no data source is open. Otherwise, equal to {@link #fileDataSource} if the open
...@@ -36,54 +68,86 @@ public final class DefaultUriDataSource implements UriDataSource { ...@@ -36,54 +68,86 @@ public final class DefaultUriDataSource implements UriDataSource {
private UriDataSource dataSource; private UriDataSource dataSource;
/** /**
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and a * Constructs a new instance.
* {@link DefaultHttpDataSource} for other URIs.
* <p> * <p>
* The constructed instance will not follow cross-protocol redirects (i.e. redirects from HTTP to * 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 * HTTPS or vice versa) when fetching remote data. Cross-protocol redirects can be enabled by
* using the {@link #DefaultUriDataSource(String, TransferListener, boolean)} constructor and * using {@link #DefaultUriDataSource(Context, TransferListener, String, boolean)} and passing
* passing {@code true} as the final argument. * {@code true} as the final argument.
* *
* @param context A context.
* @param userAgent The User-Agent string that should be used when requesting remote data. * @param userAgent The User-Agent string that should be used when requesting remote data.
* @param transferListener An optional listener.
*/ */
public DefaultUriDataSource(String userAgent, TransferListener transferListener) { public DefaultUriDataSource(Context context, String userAgent) {
this(userAgent, transferListener, false); this(context, null, userAgent, false);
} }
/** /**
* Constructs a new data source that delegates to a {@link FileDataSource} for file URIs and a * Constructs a new instance.
* {@link DefaultHttpDataSource} for other URIs. * <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 #DefaultUriDataSource(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 DefaultUriDataSource(Context context, TransferListener listener, String userAgent) {
this(context, listener, userAgent, false);
}
/**
* Constructs a new instance, optionally configured to follow cross-protocol redirects.
*
* @param context A context.
* @param listener An optional {@link TransferListener}.
* @param userAgent The User-Agent string that should be used when requesting remote data. * @param userAgent The User-Agent string that should be used when requesting remote data.
* @param transferListener An optional listener.
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
* to HTTPS and vice versa) are enabled when fetching remote data.. * to HTTPS and vice versa) are enabled when fetching remote data..
*/ */
public DefaultUriDataSource(String userAgent, TransferListener transferListener, public DefaultUriDataSource(Context context, TransferListener listener, String userAgent,
boolean allowCrossProtocolRedirects) { boolean allowCrossProtocolRedirects) {
this(new FileDataSource(transferListener), this(context, listener,
new DefaultHttpDataSource(userAgent, null, transferListener, new DefaultHttpDataSource(userAgent, null, listener,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, allowCrossProtocolRedirects)); DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, allowCrossProtocolRedirects));
} }
/** /**
* Constructs a new data source using {@code fileDataSource} for file URIs, and * Constructs a new instance, using a provided {@link HttpDataSource} for fetching remote data.
* {@code httpDataSource} for non-file URIs.
* *
* @param fileDataSource {@link UriDataSource} to use for file URIs. * @param context A context.
* @param listener An optional {@link TransferListener}.
* @param httpDataSource {@link UriDataSource} to use for non-file URIs. * @param httpDataSource {@link UriDataSource} to use for non-file URIs.
*/ */
public DefaultUriDataSource(UriDataSource fileDataSource, UriDataSource httpDataSource) { public DefaultUriDataSource(Context context, TransferListener listener,
this.fileDataSource = Assertions.checkNotNull(fileDataSource); UriDataSource httpDataSource) {
this.httpDataSource = Assertions.checkNotNull(httpDataSource); this.httpDataSource = Assertions.checkNotNull(httpDataSource);
this.fileDataSource = new FileDataSource(listener);
this.assetDataSource = new AssetDataSource(context, listener);
} }
@Override @Override
public long open(DataSpec dataSpec) throws IOException { public long open(DataSpec dataSpec) throws IOException {
Assertions.checkState(dataSource == null); Assertions.checkState(dataSource == null);
dataSource = FILE_URI_SCHEME.equals(dataSpec.uri.getScheme()) ? fileDataSource : httpDataSource; // Choose the correct source for the scheme.
String scheme = dataSpec.uri.getScheme();
if (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme)) {
dataSource = httpDataSource;
} else if (SCHEME_FILE.equals(scheme)) {
if (dataSpec.uri.getPath().startsWith("/android_asset/")) {
dataSource = assetDataSource;
} else {
dataSource = fileDataSource;
}
} else if (SCHEME_ASSET.equals(scheme)) {
dataSource = assetDataSource;
} else {
throw new UnsupportedSchemeException(scheme);
}
// Open the source and return.
return dataSource.open(dataSpec); return dataSource.open(dataSpec);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment