Allow ByteArrayDataSource to resolve the byte array when opened

This is a relatively small change, and massively simplifies the work
needed for an app to consume Kotlin Multiplatform resources (without a
full `KmpResourceDataSource` implementation, which poses some
dependency challenges for now).

Issue: androidx/media#1405
PiperOrigin-RevId: 638991375
This commit is contained in:
ibaker 2024-05-31 04:31:04 -07:00 committed by Copybara-Service
parent ac34798344
commit 4dd8360693
3 changed files with 62 additions and 35 deletions

View File

@ -145,6 +145,10 @@
* Add support for non-square DASH thumbnail grids
([#1300](https://github.com/androidx/media/pull/1300)).
* Add support for AVIF for API 34+.
* DataSource:
* Allow `ByteArrayDataSource` to resolve a URI to a byte array during
`open()`, instead of being hard-coded at construction
([#1405](https://github.com/androidx/media/issues/1405)).
* DRM:
* Allow setting a `LoadErrorHandlingPolicy` on
`DefaultDrmSessionManagerProvider`

View File

@ -15,13 +15,15 @@
*/
package androidx.media3.datasource;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static java.lang.Math.min;
import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
import java.io.IOException;
@ -29,27 +31,55 @@ import java.io.IOException;
@UnstableApi
public final class ByteArrayDataSource extends BaseDataSource {
private final byte[] data;
/** Functional interface to resolve from {@link Uri} to {@link byte[]}. */
public interface UriResolver {
/**
* Resolves a {@link Uri} to a {@link byte[]}.
*
* <p>Called during {@link DataSource#open(DataSpec)} from a loading thread, so can do blocking
* work.
*
* @return The resolved byte array.
* @throws IOException if the provided URI is not recognized, or an error occurs during
* resolution.
*/
byte[] resolve(Uri uri) throws IOException;
}
private final UriResolver uriResolver;
@Nullable private Uri uri;
@Nullable private byte[] data;
private int readPosition;
private int bytesRemaining;
private boolean opened;
/**
* Creates an instance.
*
* @param data The data to be read.
*/
public ByteArrayDataSource(byte[] data) {
this(/* uriResolver= */ unusedUri -> data);
checkArgument(data.length > 0);
}
/**
* Creates an instance.
*
* @param uriResolver Function to resolve from {@link Uri} to {@link byte[]} during {@link
* #open(DataSpec)}.
*/
public ByteArrayDataSource(UriResolver uriResolver) {
super(/* isNetwork= */ false);
Assertions.checkNotNull(data);
Assertions.checkArgument(data.length > 0);
this.data = data;
this.uriResolver = checkNotNull(uriResolver);
}
@Override
public long open(DataSpec dataSpec) throws IOException {
uri = dataSpec.uri;
transferInitializing(dataSpec);
uri = dataSpec.uri;
data = uriResolver.resolve(uri);
if (dataSpec.position > data.length) {
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
@ -72,7 +102,7 @@ public final class ByteArrayDataSource extends BaseDataSource {
}
length = min(length, bytesRemaining);
System.arraycopy(data, readPosition, buffer, offset, length);
System.arraycopy(checkStateNotNull(data), readPosition, buffer, offset, length);
readPosition += length;
bytesRemaining -= length;
bytesTransferred(length);
@ -92,5 +122,6 @@ public final class ByteArrayDataSource extends BaseDataSource {
transferEnded();
}
uri = null;
data = null;
}
}

View File

@ -20,53 +20,45 @@ import androidx.media3.test.utils.DataSourceContractTest;
import androidx.media3.test.utils.TestUtil;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import org.junit.runner.RunWith;
/** {@link DataSource} contract tests for {@link ByteArrayDataSource}. */
@RunWith(AndroidJUnit4.class)
public class ByteArrayDataSourceContractTest extends DataSourceContractTest {
private static final byte[] DATA = TestUtil.buildTestData(20);
private static final Uri URI_1 = Uri.parse("uri1");
private static final byte[] DATA_1 = TestUtil.buildTestData(20);
private static final Uri URI_2 = Uri.parse("uri2");
private static final byte[] DATA_2 = TestUtil.buildTestData(10);
@Override
protected ImmutableList<TestResource> getTestResources() {
return ImmutableList.of(
new TestResource.Builder().setName("data-1").setUri(URI_1).setExpectedBytes(DATA_1).build(),
new TestResource.Builder()
.setName("simple")
.setUri(Uri.EMPTY)
.setExpectedBytes(DATA)
.setName("data-2")
.setUri(URI_2)
.setExpectedBytes(DATA_2)
.build());
}
@Override
protected Uri getNotFoundUri() {
throw new UnsupportedOperationException();
return Uri.parse("not-found");
}
@Override
protected DataSource createDataSource() {
return new ByteArrayDataSource(DATA);
return new ByteArrayDataSource(
uri -> {
if (uri.equals(URI_1)) {
return DATA_1;
} else if (uri.equals(URI_2)) {
return DATA_2;
} else {
throw new IOException("Unrecognized URI: " + uri);
}
});
}
@Override
@Test
@Ignore
public void resourceNotFound() {}
@Override
@Test
@Ignore
public void resourceNotFound_transferListenerCallbacks() {}
@Override
@Test
@Ignore
public void getUri_resourceNotFound_returnsNullIfNotOpened() throws Exception {}
@Override
@Test
@Ignore
public void getResponseHeaders_resourceNotFound_isEmptyWhileNotOpen() throws Exception {}
}