mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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
|
* Add support for non-square DASH thumbnail grids
|
||||||
([#1300](https://github.com/androidx/media/pull/1300)).
|
([#1300](https://github.com/androidx/media/pull/1300)).
|
||||||
* Add support for AVIF for API 34+.
|
* 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:
|
* DRM:
|
||||||
* Allow setting a `LoadErrorHandlingPolicy` on
|
* Allow setting a `LoadErrorHandlingPolicy` on
|
||||||
`DefaultDrmSessionManagerProvider`
|
`DefaultDrmSessionManagerProvider`
|
||||||
|
@ -15,13 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.datasource;
|
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 static java.lang.Math.min;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
import androidx.media3.common.util.Assertions;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@ -29,27 +31,55 @@ import java.io.IOException;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class ByteArrayDataSource extends BaseDataSource {
|
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 Uri uri;
|
||||||
|
@Nullable private byte[] data;
|
||||||
private int readPosition;
|
private int readPosition;
|
||||||
private int bytesRemaining;
|
private int bytesRemaining;
|
||||||
private boolean opened;
|
private boolean opened;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
*
|
||||||
* @param data The data to be read.
|
* @param data The data to be read.
|
||||||
*/
|
*/
|
||||||
public ByteArrayDataSource(byte[] data) {
|
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);
|
super(/* isNetwork= */ false);
|
||||||
Assertions.checkNotNull(data);
|
this.uriResolver = checkNotNull(uriResolver);
|
||||||
Assertions.checkArgument(data.length > 0);
|
|
||||||
this.data = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws IOException {
|
public long open(DataSpec dataSpec) throws IOException {
|
||||||
uri = dataSpec.uri;
|
|
||||||
transferInitializing(dataSpec);
|
transferInitializing(dataSpec);
|
||||||
|
uri = dataSpec.uri;
|
||||||
|
data = uriResolver.resolve(uri);
|
||||||
if (dataSpec.position > data.length) {
|
if (dataSpec.position > data.length) {
|
||||||
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
|
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);
|
length = min(length, bytesRemaining);
|
||||||
System.arraycopy(data, readPosition, buffer, offset, length);
|
System.arraycopy(checkStateNotNull(data), readPosition, buffer, offset, length);
|
||||||
readPosition += length;
|
readPosition += length;
|
||||||
bytesRemaining -= length;
|
bytesRemaining -= length;
|
||||||
bytesTransferred(length);
|
bytesTransferred(length);
|
||||||
@ -92,5 +122,6 @@ public final class ByteArrayDataSource extends BaseDataSource {
|
|||||||
transferEnded();
|
transferEnded();
|
||||||
}
|
}
|
||||||
uri = null;
|
uri = null;
|
||||||
|
data = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,53 +20,45 @@ import androidx.media3.test.utils.DataSourceContractTest;
|
|||||||
import androidx.media3.test.utils.TestUtil;
|
import androidx.media3.test.utils.TestUtil;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.junit.Ignore;
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
/** {@link DataSource} contract tests for {@link ByteArrayDataSource}. */
|
/** {@link DataSource} contract tests for {@link ByteArrayDataSource}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class ByteArrayDataSourceContractTest extends DataSourceContractTest {
|
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
|
@Override
|
||||||
protected ImmutableList<TestResource> getTestResources() {
|
protected ImmutableList<TestResource> getTestResources() {
|
||||||
return ImmutableList.of(
|
return ImmutableList.of(
|
||||||
|
new TestResource.Builder().setName("data-1").setUri(URI_1).setExpectedBytes(DATA_1).build(),
|
||||||
new TestResource.Builder()
|
new TestResource.Builder()
|
||||||
.setName("simple")
|
.setName("data-2")
|
||||||
.setUri(Uri.EMPTY)
|
.setUri(URI_2)
|
||||||
.setExpectedBytes(DATA)
|
.setExpectedBytes(DATA_2)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Uri getNotFoundUri() {
|
protected Uri getNotFoundUri() {
|
||||||
throw new UnsupportedOperationException();
|
return Uri.parse("not-found");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DataSource createDataSource() {
|
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