Support content:// URIs, and some cleanup/consistency tweaks.

- There's definitely potential for more code sharing in these
classes, but deferring for now.
- Also made no-scheme default to file://, and allowed smoothstreaming
URLs to be specified with or without the /Manifest suffix.
This commit is contained in:
Oliver Woodman 2015-05-19 14:14:53 +01:00
parent a9c977a79e
commit 6ae97ced3a
5 changed files with 53 additions and 52 deletions

View File

@ -92,8 +92,12 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
public void buildRenderers(DemoPlayer player, RendererBuilderCallback callback) { public void buildRenderers(DemoPlayer player, RendererBuilderCallback callback) {
this.player = player; this.player = player;
this.callback = callback; this.callback = callback;
String manifestUrl = url;
if (!manifestUrl.endsWith("/Manifest")) {
manifestUrl += "/Manifest";
}
SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser(); SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser();
manifestFetcher = new ManifestFetcher<SmoothStreamingManifest>(url + "/Manifest", manifestFetcher = new ManifestFetcher<SmoothStreamingManifest>(manifestUrl,
new DefaultHttpDataSource(userAgent, null), parser); new DefaultHttpDataSource(userAgent, null), parser);
manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this); manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this);
} }

View File

@ -31,7 +31,7 @@ import java.io.InputStream;
public final class AssetDataSource implements UriDataSource { public final class AssetDataSource implements UriDataSource {
/** /**
* Thrown when IOException is encountered during local asset read operation. * Thrown when an {@link IOException} is encountered reading a local asset.
*/ */
public static final class AssetDataSourceException extends IOException { public static final class AssetDataSourceException extends IOException {
@ -45,7 +45,7 @@ public final class AssetDataSource implements UriDataSource {
private final TransferListener listener; private final TransferListener listener;
private String uriString; private String uriString;
private InputStream assetInputStream; private InputStream inputStream;
private long bytesRemaining; private long bytesRemaining;
private boolean opened; private boolean opened;
@ -76,10 +76,11 @@ public final class AssetDataSource implements UriDataSource {
} else if (path.startsWith("/")) { } else if (path.startsWith("/")) {
path = path.substring(1); path = path.substring(1);
} }
assetInputStream = assetManager.open(path, AssetManager.ACCESS_RANDOM); uriString = dataSpec.uri.toString();
long skipped = assetInputStream.skip(dataSpec.position); inputStream = assetManager.open(path, AssetManager.ACCESS_RANDOM);
long skipped = inputStream.skip(dataSpec.position);
Assertions.checkState(skipped == dataSpec.position); Assertions.checkState(skipped == dataSpec.position);
bytesRemaining = dataSpec.length == C.LENGTH_UNBOUNDED ? assetInputStream.available() bytesRemaining = dataSpec.length == C.LENGTH_UNBOUNDED ? inputStream.available()
: dataSpec.length; : dataSpec.length;
if (bytesRemaining < 0) { if (bytesRemaining < 0) {
throw new EOFException(); throw new EOFException();
@ -102,8 +103,7 @@ public final class AssetDataSource implements UriDataSource {
} else { } else {
int bytesRead = 0; int bytesRead = 0;
try { try {
bytesRead = assetInputStream.read(buffer, offset, bytesRead = inputStream.read(buffer, offset, (int) Math.min(bytesRemaining, readLength));
(int) Math.min(bytesRemaining, readLength));
} catch (IOException e) { } catch (IOException e) {
throw new AssetDataSourceException(e); throw new AssetDataSourceException(e);
} }
@ -126,13 +126,14 @@ public final class AssetDataSource implements UriDataSource {
@Override @Override
public void close() throws AssetDataSourceException { public void close() throws AssetDataSourceException {
if (assetInputStream != null) { uriString = null;
if (inputStream != null) {
try { try {
assetInputStream.close(); inputStream.close();
} catch (IOException e) { } catch (IOException e) {
throw new AssetDataSourceException(e); throw new AssetDataSourceException(e);
} finally { } finally {
assetInputStream = null; inputStream = null;
if (opened) { if (opened) {
opened = false; opened = false;
if (listener != null) { if (listener != null) {

View File

@ -15,25 +15,25 @@
*/ */
package com.google.android.exoplayer.upstream; package com.google.android.exoplayer.upstream;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.util.Assertions;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.res.AssetFileDescriptor; import android.content.res.AssetFileDescriptor;
import com.google.android.exoplayer.C; import java.io.EOFException;
import java.io.FileDescriptor;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* This calll is support content uri and file uri (e.q. content:// , file://). * A content URI {@link UriDataSource}.
* {@link DataSource}.
*/ */
public final class ContentDataSource implements UriDataSource { public final class ContentDataSource implements UriDataSource {
static private final String TAG = "ContentDataSource";
/** /**
* Thrown when IOException is encountered during local asset read operation. * Thrown when an {@link IOException} is encountered reading from a content URI.
*/ */
public static class ContentDataSourceException extends IOException { public static class ContentDataSourceException extends IOException {
@ -43,11 +43,11 @@ public final class ContentDataSource implements UriDataSource {
} }
private final ContentResolver resolver;
private final TransferListener listener; private final TransferListener listener;
private InputStream inputStream; private InputStream inputStream;
private Context context; private String uriString;
private String uri;
private long bytesRemaining; private long bytesRemaining;
private boolean opened; private boolean opened;
@ -64,23 +64,25 @@ public final class ContentDataSource 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 ContentDataSource(Context context, TransferListener listener) { public ContentDataSource(Context context, TransferListener listener) {
this.context = context; this.resolver = context.getContentResolver();
this.listener = listener; this.listener = listener;
} }
@Override @Override
public long open(DataSpec dataSpec) throws IOException { public long open(DataSpec dataSpec) throws ContentDataSourceException {
try { try {
uri = dataSpec.uri.toString(); uriString = dataSpec.uri.toString();
inputStream = new FileInputStream(getFileDescriptor(context, dataSpec)); AssetFileDescriptor assetFd = resolver.openAssetFileDescriptor(dataSpec.uri, "r");
inputStream.skip(dataSpec.position); inputStream = new FileInputStream(assetFd.getFileDescriptor());
long skipped = inputStream.skip(dataSpec.position);
Assertions.checkState(skipped == dataSpec.position);
bytesRemaining = dataSpec.length == C.LENGTH_UNBOUNDED ? inputStream.available() bytesRemaining = dataSpec.length == C.LENGTH_UNBOUNDED ? inputStream.available()
: dataSpec.length; : dataSpec.length;
if (bytesRemaining < 0) { if (bytesRemaining < 0) {
throw new IOException(); throw new EOFException();
} }
} catch (IOException e) { } catch (IOException e) {
throw new IOException(e); throw new ContentDataSourceException(e);
} }
opened = true; opened = true;
@ -92,7 +94,7 @@ public final class ContentDataSource implements UriDataSource {
} }
@Override @Override
public int read(byte[] buffer, int offset, int readLength) throws IOException { public int read(byte[] buffer, int offset, int readLength) throws ContentDataSourceException {
if (bytesRemaining == 0) { if (bytesRemaining == 0) {
return -1; return -1;
} else { } else {
@ -100,7 +102,7 @@ public final class ContentDataSource implements UriDataSource {
try { try {
bytesRead = inputStream.read(buffer, offset, (int) Math.min(bytesRemaining, readLength)); bytesRead = inputStream.read(buffer, offset, (int) Math.min(bytesRemaining, readLength));
} catch (IOException e) { } catch (IOException e) {
throw new IOException(e); throw new ContentDataSourceException(e);
} }
if (bytesRead > 0) { if (bytesRead > 0) {
@ -116,11 +118,12 @@ public final class ContentDataSource implements UriDataSource {
@Override @Override
public String getUri() { public String getUri() {
return uri; return uriString;
} }
@Override @Override
public void close() throws ContentDataSourceException { public void close() throws ContentDataSourceException {
uriString = null;
if (inputStream != null) { if (inputStream != null) {
try { try {
inputStream.close(); inputStream.close();
@ -128,8 +131,6 @@ public final class ContentDataSource implements UriDataSource {
throw new ContentDataSourceException(e); throw new ContentDataSourceException(e);
} finally { } finally {
inputStream = null; inputStream = null;
uri = null;
if (opened) { if (opened) {
opened = false; opened = false;
if (listener != null) { if (listener != null) {
@ -140,17 +141,4 @@ public final class ContentDataSource implements UriDataSource {
} }
} }
/**
* query the fileDescriptor from conten resolver.
*
*/
private static FileDescriptor getFileDescriptor(Context context, DataSpec dataSpec) throws IOException {
try {
ContentResolver resolver = context.getContentResolver();
AssetFileDescriptor fd = resolver.openAssetFileDescriptor(dataSpec.uri, "r");
return fd.getFileDescriptor();
} catch (IOException e) {
throw new IOException(e);
}
}
} }

View File

@ -18,6 +18,7 @@ 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 android.content.Context;
import android.text.TextUtils;
import java.io.IOException; import java.io.IOException;
@ -26,8 +27,11 @@ import java.io.IOException;
* *
* <ul> * <ul>
* <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4). * <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>file: For fetching data from a local file (e.g. file:///path/to/media/media.mp4, or just
* /path/to/media/media.mp4 because the implementation assumes that a URI without a scheme is a
* local file URI).
* <li>asset: For fetching data from an asset in the application's apk (e.g. asset:///media.mp4). * <li>asset: For fetching data from an asset in the application's apk (e.g. asset:///media.mp4).
* <li>content: For fetching data from a content URI (e.g. content://authority/path/123).
* </ul> * </ul>
*/ */
public final class DefaultUriDataSource implements UriDataSource { public final class DefaultUriDataSource implements UriDataSource {
@ -56,10 +60,12 @@ public final class DefaultUriDataSource implements UriDataSource {
private static final String SCHEME_HTTPS = "https"; private static final String SCHEME_HTTPS = "https";
private static final String SCHEME_FILE = "file"; private static final String SCHEME_FILE = "file";
private static final String SCHEME_ASSET = "asset"; private static final String SCHEME_ASSET = "asset";
private static final String SCHEME_CONTENT = "content";
private final UriDataSource httpDataSource; private final UriDataSource httpDataSource;
private final UriDataSource fileDataSource; private final UriDataSource fileDataSource;
private final UriDataSource assetDataSource; private final UriDataSource assetDataSource;
private final UriDataSource contentDataSource;
/** /**
* {@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
@ -127,6 +133,7 @@ public final class DefaultUriDataSource implements UriDataSource {
this.httpDataSource = Assertions.checkNotNull(httpDataSource); this.httpDataSource = Assertions.checkNotNull(httpDataSource);
this.fileDataSource = new FileDataSource(listener); this.fileDataSource = new FileDataSource(listener);
this.assetDataSource = new AssetDataSource(context, listener); this.assetDataSource = new AssetDataSource(context, listener);
this.contentDataSource = new ContentDataSource(context, listener);
} }
@Override @Override
@ -136,7 +143,7 @@ public final class DefaultUriDataSource implements UriDataSource {
String scheme = dataSpec.uri.getScheme(); String scheme = dataSpec.uri.getScheme();
if (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme)) { if (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme)) {
dataSource = httpDataSource; dataSource = httpDataSource;
} else if (SCHEME_FILE.equals(scheme)) { } else if (SCHEME_FILE.equals(scheme) || TextUtils.isEmpty(scheme)) {
if (dataSpec.uri.getPath().startsWith("/android_asset/")) { if (dataSpec.uri.getPath().startsWith("/android_asset/")) {
dataSource = assetDataSource; dataSource = assetDataSource;
} else { } else {
@ -144,6 +151,8 @@ public final class DefaultUriDataSource implements UriDataSource {
} }
} else if (SCHEME_ASSET.equals(scheme)) { } else if (SCHEME_ASSET.equals(scheme)) {
dataSource = assetDataSource; dataSource = assetDataSource;
} else if (SCHEME_CONTENT.equals(scheme)) {
dataSource = contentDataSource;
} else { } else {
throw new UnsupportedSchemeException(scheme); throw new UnsupportedSchemeException(scheme);
} }

View File

@ -40,7 +40,7 @@ public final class FileDataSource implements UriDataSource {
private final TransferListener listener; private final TransferListener listener;
private RandomAccessFile file; private RandomAccessFile file;
private String uri; private String uriString;
private long bytesRemaining; private long bytesRemaining;
private boolean opened; private boolean opened;
@ -63,7 +63,7 @@ public final class FileDataSource implements UriDataSource {
@Override @Override
public long open(DataSpec dataSpec) throws FileDataSourceException { public long open(DataSpec dataSpec) throws FileDataSourceException {
try { try {
uri = dataSpec.uri.toString(); uriString = dataSpec.uri.toString();
file = new RandomAccessFile(dataSpec.uri.getPath(), "r"); file = new RandomAccessFile(dataSpec.uri.getPath(), "r");
file.seek(dataSpec.position); file.seek(dataSpec.position);
bytesRemaining = dataSpec.length == C.LENGTH_UNBOUNDED ? file.length() - dataSpec.position bytesRemaining = dataSpec.length == C.LENGTH_UNBOUNDED ? file.length() - dataSpec.position
@ -108,11 +108,12 @@ public final class FileDataSource implements UriDataSource {
@Override @Override
public String getUri() { public String getUri() {
return uri; return uriString;
} }
@Override @Override
public void close() throws FileDataSourceException { public void close() throws FileDataSourceException {
uriString = null;
if (file != null) { if (file != null) {
try { try {
file.close(); file.close();
@ -120,8 +121,6 @@ public final class FileDataSource implements UriDataSource {
throw new FileDataSourceException(e); throw new FileDataSourceException(e);
} finally { } finally {
file = null; file = null;
uri = null;
if (opened) { if (opened) {
opened = false; opened = false;
if (listener != null) { if (listener != null) {