Commit 14d3ed09 by olly Committed by Oliver Woodman

Add DataSpec.Builder

PiperOrigin-RevId: 294518763
parent 7a849e11
......@@ -19,6 +19,7 @@
* Move player message-related constants from `C` to `Renderer`, to avoid
having the constants class depend on player/renderer classes.
* Split out `common` and `extractor` submodules.
* Add `DataSpec.Builder`.
* Text:
* Parse `<ruby>` and `<rt>` tags in WebVTT subtitles (rendering is coming
later).
......
......@@ -924,16 +924,12 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
// For POST redirects that aren't 307 or 308, the redirect is followed but request is
// transformed into a GET.
redirectUrlDataSpec =
new DataSpec(
Uri.parse(newLocationUrl),
DataSpec.HTTP_METHOD_GET,
/* httpBody= */ null,
dataSpec.absoluteStreamPosition,
dataSpec.position,
dataSpec.length,
dataSpec.key,
dataSpec.flags,
dataSpec.httpRequestHeaders);
dataSpec
.buildUpon()
.setUri(Uri.parse(newLocationUrl))
.setHttpMethod(DataSpec.HTTP_METHOD_GET)
.setHttpBody(null)
.build();
} else {
redirectUrlDataSpec = dataSpec.withUri(Uri.parse(newLocationUrl));
}
......
......@@ -32,6 +32,163 @@ import java.util.Map;
*/
public final class DataSpec {
/** Builds {@link DataSpec} instances. */
public static final class Builder {
@Nullable private Uri uri;
private long uriPositionOffset;
@HttpMethod private int httpMethod;
@Nullable private byte[] httpBody;
private Map<String, String> httpRequestHeaders;
private long position;
private long length;
@Nullable private String key;
@Flags private int flags;
/** Creates a new instance with default values. */
public Builder() {
httpMethod = HTTP_METHOD_GET;
httpRequestHeaders = Collections.emptyMap();
length = C.LENGTH_UNSET;
}
/**
* Creates a new instance to build upon the provided {@link DataSpec}.
*
* @param dataSpec The {@link DataSpec} to build upon.
*/
private Builder(DataSpec dataSpec) {
uri = dataSpec.uri;
uriPositionOffset = dataSpec.uriPositionOffset;
httpMethod = dataSpec.httpMethod;
httpBody = dataSpec.httpBody;
httpRequestHeaders = dataSpec.httpRequestHeaders;
position = dataSpec.position;
length = dataSpec.length;
key = dataSpec.key;
flags = dataSpec.flags;
}
/**
* Sets {@link DataSpec#uri}. Must be called before {@link #build()}.
*
* @param uri The {@link DataSpec#uri}.
* @return The builder.
*/
public Builder setUri(Uri uri) {
this.uri = uri;
return this;
}
/**
* Sets the {@link DataSpec#uriPositionOffset}. The default value is 0.
*
* @param uriPositionOffset The {@link DataSpec#uriPositionOffset}.
* @return The builder.
*/
public Builder setUriPositionOffset(long uriPositionOffset) {
this.uriPositionOffset = uriPositionOffset;
return this;
}
/**
* Sets {@link DataSpec#httpMethod}. The default value is {@link #HTTP_METHOD_GET}.
*
* @param httpMethod The {@link DataSpec#httpMethod}.
* @return The builder.
*/
public Builder setHttpMethod(@HttpMethod int httpMethod) {
this.httpMethod = httpMethod;
return this;
}
/**
* Sets {@link DataSpec#httpBody}. The default value is {@code null}.
*
* @param httpBody The {@link DataSpec#httpBody}.
* @return The builder.
*/
public Builder setHttpBody(@Nullable byte[] httpBody) {
this.httpBody = httpBody;
return this;
}
/**
* Sets the {@link DataSpec#httpRequestHeaders}. The default value is an empty map.
*
* @param httpRequestHeaders The {@link DataSpec#httpRequestHeaders}.
* @return The builder.
*/
public Builder setHttpRequestHeaders(Map<String, String> httpRequestHeaders) {
this.httpRequestHeaders = httpRequestHeaders;
return this;
}
/**
* Sets the {@link DataSpec#position}. The default value is 0.
*
* @param position The {@link DataSpec#position}.
* @return The builder.
*/
public Builder setPosition(long position) {
this.position = position;
return this;
}
/**
* Sets the {@link DataSpec#length}. The default value is {@link C#LENGTH_UNSET}.
*
* @param length The {@link DataSpec#length}.
* @return The builder.
*/
public Builder setLength(long length) {
this.length = length;
return this;
}
/**
* Sets the {@link DataSpec#key}. The default value is {@code null}.
*
* @param key The {@link DataSpec#key}.
* @return The builder.
*/
public Builder setKey(@Nullable String key) {
this.key = key;
return this;
}
/**
* Sets the {@link DataSpec#flags}. The default value is 0.
*
* @param flags The {@link DataSpec#flags}.
* @return The builder.
*/
public Builder setFlags(@Flags int flags) {
this.flags = flags;
return this;
}
/**
* Builds a {@link DataSpec} with the builder's current values.
*
* @return The build {@link DataSpec}.
* @throws IllegalStateException If {@link #setUri(Uri)} has not been called.
*/
public DataSpec build() {
Assertions.checkStateNotNull(uri, "The uri must be set.");
return new DataSpec(
uri,
uriPositionOffset,
httpMethod,
httpBody,
httpRequestHeaders,
position,
length,
key,
flags);
}
}
/**
* The flags that apply to any request for data. Possible flag values are {@link
* #FLAG_ALLOW_GZIP}, {@link #FLAG_DONT_CACHE_IF_LENGTH_UNKNOWN}, {@link
......@@ -131,7 +288,7 @@ public final class DataSpec {
* The HTTP method to use when requesting the data. This value will be ignored by non-HTTP {@link
* DataSource} implementations.
*/
public final @HttpMethod int httpMethod;
@HttpMethod public final int httpMethod;
/**
* The HTTP request body, null otherwise. If the body is non-null, then {@code httpBody.length}
......@@ -166,7 +323,7 @@ public final class DataSpec {
@Nullable public final String key;
/** Request {@link Flags flags}. */
public final @Flags int flags;
@Flags public final int flags;
/**
* Constructs an instance.
......@@ -408,6 +565,11 @@ public final class DataSpec {
return getStringForHttpMethod(httpMethod);
}
/** Returns a {@link DataSpec.Builder} initialized with the values of this instance. */
public DataSpec.Builder buildUpon() {
return new Builder(this);
}
/**
* Returns a data spec that represents a subrange of the data defined by this DataSpec. The
* subrange includes data from the offset up to the end of this DataSpec.
......
......@@ -94,13 +94,19 @@ public class DataSpecTest {
assertDefaultDataSpec(dataSpec, uri);
}
@SuppressWarnings("deprecation")
@Test
public void createDataSpec_setsCustomValues() {
public void createDataSpec_withBuilder_withDefaultValues() {
Uri uri = Uri.parse("www.google.com");
Map<String, String> httpRequestHeaders = createHttpRequestHeaders(3);
DataSpec dataSpec = new DataSpec.Builder().setUri(uri).build();
assertDefaultDataSpec(dataSpec, uri);
}
@SuppressWarnings("deprecation")
@Test
public void createDataSpec_setsValues() {
Uri uri = Uri.parse("www.google.com");
Map<String, String> httpRequestHeaders = createHttpRequestHeaders(3);
byte[] httpBody = new byte[] {0, 1, 2, 3};
DataSpec dataSpec =
......@@ -129,6 +135,77 @@ public class DataSpecTest {
assertHttpRequestHeadersReadOnly(dataSpec);
}
@SuppressWarnings("deprecation")
@Test
public void createDataSpec_withBuilder_setsValues() {
Uri uri = Uri.parse("www.google.com");
Map<String, String> httpRequestHeaders = createHttpRequestHeaders(3);
byte[] httpBody = new byte[] {0, 1, 2, 3};
DataSpec dataSpec =
new DataSpec.Builder()
.setUri(uri)
.setUriPositionOffset(50)
.setHttpMethod(DataSpec.HTTP_METHOD_POST)
.setHttpBody(httpBody)
.setPosition(150)
.setLength(5)
.setKey("key")
.setFlags(DataSpec.FLAG_ALLOW_GZIP)
.setHttpRequestHeaders(httpRequestHeaders)
.build();
assertThat(dataSpec.uri).isEqualTo(uri);
assertThat(dataSpec.uriPositionOffset).isEqualTo(50);
assertThat(dataSpec.httpMethod).isEqualTo(DataSpec.HTTP_METHOD_POST);
assertThat(dataSpec.httpBody).isEqualTo(httpBody);
assertThat(dataSpec.httpRequestHeaders).isEqualTo(httpRequestHeaders);
// absoluteStreamPosition = uriPositionOffset + position
assertThat(dataSpec.absoluteStreamPosition).isEqualTo(200);
assertThat(dataSpec.position).isEqualTo(150);
assertThat(dataSpec.length).isEqualTo(5);
assertThat(dataSpec.key).isEqualTo("key");
assertThat(dataSpec.flags).isEqualTo(DataSpec.FLAG_ALLOW_GZIP);
assertHttpRequestHeadersReadOnly(dataSpec);
}
@SuppressWarnings("deprecation")
@Test
public void buildUponDataSpec_setsValues() {
Uri uri = Uri.parse("www.google.com");
Map<String, String> httpRequestHeaders = createHttpRequestHeaders(3);
byte[] httpBody = new byte[] {0, 1, 2, 3};
DataSpec dataSpec =
new DataSpec.Builder()
.setUri(uri)
.setUriPositionOffset(50)
.setHttpMethod(DataSpec.HTTP_METHOD_POST)
.setHttpBody(httpBody)
.setPosition(150)
.setLength(5)
.setKey("key")
.setFlags(DataSpec.FLAG_ALLOW_GZIP)
.setHttpRequestHeaders(httpRequestHeaders)
.build();
// Build upon the DataSpec.
dataSpec = dataSpec.buildUpon().build();
assertThat(dataSpec.uri).isEqualTo(uri);
assertThat(dataSpec.uriPositionOffset).isEqualTo(50);
assertThat(dataSpec.httpMethod).isEqualTo(DataSpec.HTTP_METHOD_POST);
assertThat(dataSpec.httpBody).isEqualTo(httpBody);
assertThat(dataSpec.httpRequestHeaders).isEqualTo(httpRequestHeaders);
// absoluteStreamPosition = uriPositionOffset + position
assertThat(dataSpec.absoluteStreamPosition).isEqualTo(200);
assertThat(dataSpec.position).isEqualTo(150);
assertThat(dataSpec.length).isEqualTo(5);
assertThat(dataSpec.key).isEqualTo("key");
assertThat(dataSpec.flags).isEqualTo(DataSpec.FLAG_ALLOW_GZIP);
assertHttpRequestHeadersReadOnly(dataSpec);
}
@Test
public void createDataSpec_setsHttpMethodAndPostBody() {
Uri uri = Uri.parse("www.google.com");
......
......@@ -23,7 +23,6 @@ import com.google.android.exoplayer2.upstream.DataSink;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSourceException;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod;
import com.google.android.exoplayer2.upstream.FileDataSource;
import com.google.android.exoplayer2.upstream.TeeDataSource;
import com.google.android.exoplayer2.upstream.TransferListener;
......@@ -132,15 +131,10 @@ public final class CacheDataSource implements DataSource {
private final boolean ignoreCacheOnError;
private final boolean ignoreCacheForUnsetLengthRequests;
@Nullable private Uri actualUri;
@Nullable private DataSpec requestDataSpec;
@Nullable private DataSource currentDataSource;
private boolean currentDataSpecLengthUnset;
@Nullable private Uri uri;
@Nullable private Uri actualUri;
@HttpMethod private int httpMethod;
@Nullable private byte[] httpBody;
private Map<String, String> httpRequestHeaders = Collections.emptyMap();
@DataSpec.Flags private int flags;
@Nullable private String key;
private long readPosition;
private long bytesRemaining;
@Nullable private CacheSpan currentHoleSpan;
......@@ -259,13 +253,9 @@ public final class CacheDataSource implements DataSource {
@Override
public long open(DataSpec dataSpec) throws IOException {
try {
key = cacheKeyFactory.buildCacheKey(dataSpec);
uri = dataSpec.uri;
actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri);
httpMethod = dataSpec.httpMethod;
httpBody = dataSpec.httpBody;
httpRequestHeaders = dataSpec.httpRequestHeaders;
flags = dataSpec.flags;
String key = cacheKeyFactory.buildCacheKey(dataSpec);
requestDataSpec = dataSpec.buildUpon().setKey(key).build();
actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ requestDataSpec.uri);
readPosition = dataSpec.position;
int reason = shouldIgnoreCacheForRequest(dataSpec);
......@@ -351,14 +341,9 @@ public final class CacheDataSource implements DataSource {
@Override
public void close() throws IOException {
uri = null;
requestDataSpec = null;
actualUri = null;
httpMethod = DataSpec.HTTP_METHOD_GET;
httpBody = null;
httpRequestHeaders = Collections.emptyMap();
flags = 0;
readPosition = 0;
key = null;
notifyBytesRead();
try {
closeCurrentSource();
......@@ -384,6 +369,7 @@ public final class CacheDataSource implements DataSource {
*/
private void openNextSource(boolean checkCache) throws IOException {
@Nullable CacheSpan nextSpan;
String key = requestDataSpec.key;
if (currentRequestIgnoresCache) {
nextSpan = null;
} else if (blockOnCache) {
......@@ -404,27 +390,24 @@ public final class CacheDataSource implements DataSource {
// from upstream.
nextDataSource = upstreamDataSource;
nextDataSpec =
new DataSpec(
uri,
httpMethod,
httpBody,
readPosition,
readPosition,
bytesRemaining,
key,
flags,
httpRequestHeaders);
requestDataSpec.buildUpon().setPosition(readPosition).setLength(bytesRemaining).build();
} else if (nextSpan.isCached) {
// Data is cached, read from cache.
// Data is cached in a span file starting at nextSpan.position.
Uri fileUri = Uri.fromFile(nextSpan.file);
long filePosition = readPosition - nextSpan.position;
long length = nextSpan.length - filePosition;
long filePositionOffset = nextSpan.position;
long positionInFile = readPosition - filePositionOffset;
long length = nextSpan.length - positionInFile;
if (bytesRemaining != C.LENGTH_UNSET) {
length = Math.min(length, bytesRemaining);
}
// Deliberately skip the HTTP-related parameters since we're reading from the cache, not
// making an HTTP request.
nextDataSpec = new DataSpec(fileUri, readPosition, filePosition, length, key, flags);
nextDataSpec =
requestDataSpec
.buildUpon()
.setUri(fileUri)
.setUriPositionOffset(filePositionOffset)
.setPosition(positionInFile)
.setLength(length)
.build();
nextDataSource = cacheReadDataSource;
} else {
// Data is not cached, and data is not locked, read from upstream with cache backing.
......@@ -438,16 +421,7 @@ public final class CacheDataSource implements DataSource {
}
}
nextDataSpec =
new DataSpec(
uri,
httpMethod,
httpBody,
readPosition,
readPosition,
length,
key,
flags,
httpRequestHeaders);
requestDataSpec.buildUpon().setPosition(readPosition).setLength(length).build();
if (cacheWriteDataSource != null) {
nextDataSource = cacheWriteDataSource;
} else {
......@@ -494,7 +468,7 @@ public final class CacheDataSource implements DataSource {
}
if (isReadingFromUpstream()) {
actualUri = currentDataSource.getUri();
boolean isRedirected = !uri.equals(actualUri);
boolean isRedirected = !requestDataSpec.uri.equals(actualUri);
ContentMetadataMutations.setRedirectedUri(mutations, isRedirected ? actualUri : null);
}
if (isWritingToCache()) {
......@@ -507,7 +481,7 @@ public final class CacheDataSource implements DataSource {
if (isWritingToCache()) {
ContentMetadataMutations mutations = new ContentMetadataMutations();
ContentMetadataMutations.setContentLength(mutations, readPosition);
cache.applyContentMetadataMutations(key, mutations);
cache.applyContentMetadataMutations(requestDataSpec.key, mutations);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment