Skip to content

Commit 4dd8360

Browse files
icbakercopybara-github
authored andcommitted
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: #1405 PiperOrigin-RevId: 638991375
1 parent ac34798 commit 4dd8360

File tree

3 files changed

+62
-35
lines changed

3 files changed

+62
-35
lines changed

RELEASENOTES.md

+4
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@
145145
* Add support for non-square DASH thumbnail grids
146146
([#1300](https://github.com/androidx/media/pull/1300)).
147147
* Add support for AVIF for API 34+.
148+
* DataSource:
149+
* Allow `ByteArrayDataSource` to resolve a URI to a byte array during
150+
`open()`, instead of being hard-coded at construction
151+
([#1405](https://github.com/androidx/media/issues/1405)).
148152
* DRM:
149153
* Allow setting a `LoadErrorHandlingPolicy` on
150154
`DefaultDrmSessionManagerProvider`

libraries/datasource/src/main/java/androidx/media3/datasource/ByteArrayDataSource.java

+38-7
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,71 @@
1515
*/
1616
package androidx.media3.datasource;
1717

18+
import static androidx.media3.common.util.Assertions.checkArgument;
19+
import static androidx.media3.common.util.Assertions.checkNotNull;
20+
import static androidx.media3.common.util.Assertions.checkStateNotNull;
1821
import static java.lang.Math.min;
1922

2023
import android.net.Uri;
2124
import androidx.annotation.Nullable;
2225
import androidx.media3.common.C;
2326
import androidx.media3.common.PlaybackException;
24-
import androidx.media3.common.util.Assertions;
2527
import androidx.media3.common.util.UnstableApi;
2628
import java.io.IOException;
2729

2830
/** A {@link DataSource} for reading from a byte array. */
2931
@UnstableApi
3032
public final class ByteArrayDataSource extends BaseDataSource {
3133

32-
private final byte[] data;
34+
/** Functional interface to resolve from {@link Uri} to {@link byte[]}. */
35+
public interface UriResolver {
36+
/**
37+
* Resolves a {@link Uri} to a {@link byte[]}.
38+
*
39+
* <p>Called during {@link DataSource#open(DataSpec)} from a loading thread, so can do blocking
40+
* work.
41+
*
42+
* @return The resolved byte array.
43+
* @throws IOException if the provided URI is not recognized, or an error occurs during
44+
* resolution.
45+
*/
46+
byte[] resolve(Uri uri) throws IOException;
47+
}
48+
49+
private final UriResolver uriResolver;
3350

3451
@Nullable private Uri uri;
52+
@Nullable private byte[] data;
3553
private int readPosition;
3654
private int bytesRemaining;
3755
private boolean opened;
3856

3957
/**
58+
* Creates an instance.
59+
*
4060
* @param data The data to be read.
4161
*/
4262
public ByteArrayDataSource(byte[] data) {
63+
this(/* uriResolver= */ unusedUri -> data);
64+
checkArgument(data.length > 0);
65+
}
66+
67+
/**
68+
* Creates an instance.
69+
*
70+
* @param uriResolver Function to resolve from {@link Uri} to {@link byte[]} during {@link
71+
* #open(DataSpec)}.
72+
*/
73+
public ByteArrayDataSource(UriResolver uriResolver) {
4374
super(/* isNetwork= */ false);
44-
Assertions.checkNotNull(data);
45-
Assertions.checkArgument(data.length > 0);
46-
this.data = data;
75+
this.uriResolver = checkNotNull(uriResolver);
4776
}
4877

4978
@Override
5079
public long open(DataSpec dataSpec) throws IOException {
51-
uri = dataSpec.uri;
5280
transferInitializing(dataSpec);
81+
uri = dataSpec.uri;
82+
data = uriResolver.resolve(uri);
5383
if (dataSpec.position > data.length) {
5484
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
5585
}
@@ -72,7 +102,7 @@ public int read(byte[] buffer, int offset, int length) {
72102
}
73103

74104
length = min(length, bytesRemaining);
75-
System.arraycopy(data, readPosition, buffer, offset, length);
105+
System.arraycopy(checkStateNotNull(data), readPosition, buffer, offset, length);
76106
readPosition += length;
77107
bytesRemaining -= length;
78108
bytesTransferred(length);
@@ -92,5 +122,6 @@ public void close() {
92122
transferEnded();
93123
}
94124
uri = null;
125+
data = null;
95126
}
96127
}

libraries/datasource/src/test/java/androidx/media3/datasource/ByteArrayDataSourceContractTest.java

+20-28
Original file line numberDiff line numberDiff line change
@@ -20,53 +20,45 @@
2020
import androidx.media3.test.utils.TestUtil;
2121
import androidx.test.ext.junit.runners.AndroidJUnit4;
2222
import com.google.common.collect.ImmutableList;
23-
import org.junit.Ignore;
24-
import org.junit.Test;
23+
import java.io.IOException;
2524
import org.junit.runner.RunWith;
2625

2726
/** {@link DataSource} contract tests for {@link ByteArrayDataSource}. */
2827
@RunWith(AndroidJUnit4.class)
2928
public class ByteArrayDataSourceContractTest extends DataSourceContractTest {
3029

31-
private static final byte[] DATA = TestUtil.buildTestData(20);
30+
private static final Uri URI_1 = Uri.parse("uri1");
31+
private static final byte[] DATA_1 = TestUtil.buildTestData(20);
32+
private static final Uri URI_2 = Uri.parse("uri2");
33+
private static final byte[] DATA_2 = TestUtil.buildTestData(10);
3234

3335
@Override
3436
protected ImmutableList<TestResource> getTestResources() {
3537
return ImmutableList.of(
38+
new TestResource.Builder().setName("data-1").setUri(URI_1).setExpectedBytes(DATA_1).build(),
3639
new TestResource.Builder()
37-
.setName("simple")
38-
.setUri(Uri.EMPTY)
39-
.setExpectedBytes(DATA)
40+
.setName("data-2")
41+
.setUri(URI_2)
42+
.setExpectedBytes(DATA_2)
4043
.build());
4144
}
4245

4346
@Override
4447
protected Uri getNotFoundUri() {
45-
throw new UnsupportedOperationException();
48+
return Uri.parse("not-found");
4649
}
4750

4851
@Override
4952
protected DataSource createDataSource() {
50-
return new ByteArrayDataSource(DATA);
53+
return new ByteArrayDataSource(
54+
uri -> {
55+
if (uri.equals(URI_1)) {
56+
return DATA_1;
57+
} else if (uri.equals(URI_2)) {
58+
return DATA_2;
59+
} else {
60+
throw new IOException("Unrecognized URI: " + uri);
61+
}
62+
});
5163
}
52-
53-
@Override
54-
@Test
55-
@Ignore
56-
public void resourceNotFound() {}
57-
58-
@Override
59-
@Test
60-
@Ignore
61-
public void resourceNotFound_transferListenerCallbacks() {}
62-
63-
@Override
64-
@Test
65-
@Ignore
66-
public void getUri_resourceNotFound_returnsNullIfNotOpened() throws Exception {}
67-
68-
@Override
69-
@Test
70-
@Ignore
71-
public void getResponseHeaders_resourceNotFound_isEmptyWhileNotOpen() throws Exception {}
7264
}

0 commit comments

Comments
 (0)