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:
parent
a9c977a79e
commit
6ae97ced3a
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user