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:
parent
ac34798344
commit
4dd8360693
@ -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`
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user