Support android.resource URI scheme
Issue: #7866 PiperOrigin-RevId: 330736774
This commit is contained in:
parent
8955cd3a61
commit
97a0df77f6
@ -1,5 +1,11 @@
|
||||
# Release notes
|
||||
|
||||
### 2.12.1 ###
|
||||
|
||||
* Data sources:
|
||||
* Add support for `android.resource` URI scheme in `RawResourceDataSource`
|
||||
([#7866](https://github.com/google/ExoPlayer/issues/7866)).
|
||||
|
||||
### 2.12.0 (2020-09-11) ###
|
||||
|
||||
To learn more about what's new in 2.12, read the corresponding
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -39,6 +40,9 @@ import java.util.Map;
|
||||
* <li>rawresource: For fetching data from a raw resource in the application's apk (e.g.
|
||||
* rawresource:///resourceId, where rawResourceId is the integer identifier of the raw
|
||||
* resource).
|
||||
* <li>android.resource: For fetching data in the application's apk (e.g.
|
||||
* android.resource:///resourceId or android.resource://resourceType/resourceName). See {@link
|
||||
* RawResourceDataSource} for more information about the URI form.
|
||||
* <li>content: For fetching data from a content URI (e.g. content://authority/path/123).
|
||||
* <li>rtmp: For fetching data over RTMP. Only supported if the project using ExoPlayer has an
|
||||
* explicit dependency on ExoPlayer's RTMP extension.
|
||||
@ -58,7 +62,9 @@ public final class DefaultDataSource implements DataSource {
|
||||
private static final String SCHEME_CONTENT = "content";
|
||||
private static final String SCHEME_RTMP = "rtmp";
|
||||
private static final String SCHEME_UDP = "udp";
|
||||
private static final String SCHEME_DATA = DataSchemeDataSource.SCHEME_DATA;
|
||||
private static final String SCHEME_RAW = RawResourceDataSource.RAW_RESOURCE_SCHEME;
|
||||
private static final String SCHEME_ANDROID_RESOURCE = ContentResolver.SCHEME_ANDROID_RESOURCE;
|
||||
|
||||
private final Context context;
|
||||
private final List<TransferListener> transferListeners;
|
||||
@ -182,9 +188,9 @@ public final class DefaultDataSource implements DataSource {
|
||||
dataSource = getRtmpDataSource();
|
||||
} else if (SCHEME_UDP.equals(scheme)) {
|
||||
dataSource = getUdpDataSource();
|
||||
} else if (DataSchemeDataSource.SCHEME_DATA.equals(scheme)) {
|
||||
} else if (SCHEME_DATA.equals(scheme)) {
|
||||
dataSource = getDataSchemeDataSource();
|
||||
} else if (SCHEME_RAW.equals(scheme)) {
|
||||
} else if (SCHEME_RAW.equals(scheme) || SCHEME_ANDROID_RESOURCE.equals(scheme)) {
|
||||
dataSource = getRawResourceDataSource();
|
||||
} else {
|
||||
dataSource = baseDataSource;
|
||||
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.upstream;
|
||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Resources;
|
||||
@ -34,9 +35,20 @@ import java.io.InputStream;
|
||||
/**
|
||||
* A {@link DataSource} for reading a raw resource inside the APK.
|
||||
*
|
||||
* <p>URIs supported by this source are of the form {@code rawresource:///rawResourceId}, where
|
||||
* rawResourceId is the integer identifier of a raw resource. {@link #buildRawResourceUri(int)} can
|
||||
* be used to build {@link Uri}s in this format.
|
||||
* <p>URIs supported by this source are of one of the forms:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code rawresource:///id}, where {@code id} is the integer identifier of a raw resource.
|
||||
* <li>{@code android.resource:///id}, where {@code id} is the integer identifier of a raw
|
||||
* resource.
|
||||
* <li>{@code android.resource://[package]/[type/]name}, where {@code package} is the name of the
|
||||
* package in which the resource is located, {@code type} is the resource type and {@code
|
||||
* name} is the resource name. The package and the type are optional. Their default value is
|
||||
* the package of this application and "raw", respectively. Using the two other forms is more
|
||||
* efficient.
|
||||
* </ul>
|
||||
*
|
||||
* <p>{@link #buildRawResourceUri(int)} can be used to build supported {@link Uri}s.
|
||||
*/
|
||||
public final class RawResourceDataSource extends BaseDataSource {
|
||||
|
||||
@ -67,6 +79,7 @@ public final class RawResourceDataSource extends BaseDataSource {
|
||||
public static final String RAW_RESOURCE_SCHEME = "rawresource";
|
||||
|
||||
private final Resources resources;
|
||||
private final String packageName;
|
||||
|
||||
@Nullable private Uri uri;
|
||||
@Nullable private AssetFileDescriptor assetFileDescriptor;
|
||||
@ -80,33 +93,55 @@ public final class RawResourceDataSource extends BaseDataSource {
|
||||
public RawResourceDataSource(Context context) {
|
||||
super(/* isNetwork= */ false);
|
||||
this.resources = context.getResources();
|
||||
this.packageName = context.getPackageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long open(DataSpec dataSpec) throws RawResourceDataSourceException {
|
||||
try {
|
||||
Uri uri = dataSpec.uri;
|
||||
this.uri = uri;
|
||||
if (!TextUtils.equals(RAW_RESOURCE_SCHEME, uri.getScheme())) {
|
||||
throw new RawResourceDataSourceException("URI must use scheme " + RAW_RESOURCE_SCHEME);
|
||||
}
|
||||
Uri uri = dataSpec.uri;
|
||||
this.uri = uri;
|
||||
|
||||
int resourceId;
|
||||
int resourceId;
|
||||
if (TextUtils.equals(RAW_RESOURCE_SCHEME, uri.getScheme())
|
||||
|| (TextUtils.equals(ContentResolver.SCHEME_ANDROID_RESOURCE, uri.getScheme())
|
||||
&& uri.getPathSegments().size() == 1
|
||||
&& Assertions.checkNotNull(uri.getLastPathSegment()).matches("\\d+"))) {
|
||||
try {
|
||||
resourceId = Integer.parseInt(Assertions.checkNotNull(uri.getLastPathSegment()));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new RawResourceDataSourceException("Resource identifier must be an integer.");
|
||||
}
|
||||
|
||||
transferInitializing(dataSpec);
|
||||
AssetFileDescriptor assetFileDescriptor = resources.openRawResourceFd(resourceId);
|
||||
this.assetFileDescriptor = assetFileDescriptor;
|
||||
if (assetFileDescriptor == null) {
|
||||
throw new RawResourceDataSourceException("Resource is compressed: " + uri);
|
||||
} else if (TextUtils.equals(ContentResolver.SCHEME_ANDROID_RESOURCE, uri.getScheme())) {
|
||||
String path = Assertions.checkNotNull(uri.getPath());
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
FileInputStream inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor());
|
||||
this.inputStream = inputStream;
|
||||
@Nullable String host = uri.getHost();
|
||||
String resourceName = (TextUtils.isEmpty(host) ? "" : (host + ":")) + path;
|
||||
resourceId =
|
||||
resources.getIdentifier(
|
||||
resourceName, /* defType= */ "raw", /* defPackage= */ packageName);
|
||||
if (resourceId == 0) {
|
||||
throw new RawResourceDataSourceException("Resource not found.");
|
||||
}
|
||||
} else {
|
||||
throw new RawResourceDataSourceException(
|
||||
"URI must either use scheme "
|
||||
+ RAW_RESOURCE_SCHEME
|
||||
+ " or "
|
||||
+ ContentResolver.SCHEME_ANDROID_RESOURCE);
|
||||
}
|
||||
|
||||
transferInitializing(dataSpec);
|
||||
AssetFileDescriptor assetFileDescriptor = resources.openRawResourceFd(resourceId);
|
||||
this.assetFileDescriptor = assetFileDescriptor;
|
||||
if (assetFileDescriptor == null) {
|
||||
throw new RawResourceDataSourceException("Resource is compressed: " + uri);
|
||||
}
|
||||
|
||||
FileInputStream inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor());
|
||||
this.inputStream = inputStream;
|
||||
try {
|
||||
inputStream.skip(assetFileDescriptor.getStartOffset());
|
||||
long skipped = inputStream.skip(dataSpec.position);
|
||||
if (skipped < dataSpec.position) {
|
||||
@ -114,18 +149,21 @@ public final class RawResourceDataSource extends BaseDataSource {
|
||||
// skip beyond the end of the data.
|
||||
throw new EOFException();
|
||||
}
|
||||
if (dataSpec.length != C.LENGTH_UNSET) {
|
||||
bytesRemaining = dataSpec.length;
|
||||
} else {
|
||||
long assetFileDescriptorLength = assetFileDescriptor.getLength();
|
||||
// If the length is UNKNOWN_LENGTH then the asset extends to the end of the file.
|
||||
bytesRemaining = assetFileDescriptorLength == AssetFileDescriptor.UNKNOWN_LENGTH
|
||||
? C.LENGTH_UNSET : (assetFileDescriptorLength - dataSpec.position);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RawResourceDataSourceException(e);
|
||||
}
|
||||
|
||||
if (dataSpec.length != C.LENGTH_UNSET) {
|
||||
bytesRemaining = dataSpec.length;
|
||||
} else {
|
||||
long assetFileDescriptorLength = assetFileDescriptor.getLength();
|
||||
// If the length is UNKNOWN_LENGTH then the asset extends to the end of the file.
|
||||
bytesRemaining =
|
||||
assetFileDescriptorLength == AssetFileDescriptor.UNKNOWN_LENGTH
|
||||
? C.LENGTH_UNSET
|
||||
: (assetFileDescriptorLength - dataSpec.position);
|
||||
}
|
||||
|
||||
opened = true;
|
||||
transferStarted(dataSpec);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user