diff --git a/RELEASENOTES.md b/RELEASENOTES.md index aa61bbe0e6..61a4a6900c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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 diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultDataSource.java index 7efa89eaa0..12fea3898c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultDataSource.java @@ -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; *
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. + *
URIs supported by this source are of one of the forms: + * + *
{@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);