diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java index 5aa0efe5e2..82a9f98c51 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java @@ -438,13 +438,14 @@ public class HlsChunkSource { private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) { Uri mediaPlaylistUri = Util.getMergedUri(baseUri, enabledVariants[variantIndex].url); - DataSpec dataSpec = new DataSpec(mediaPlaylistUri, 0, C.LENGTH_UNBOUNDED, null); + DataSpec dataSpec = new DataSpec(mediaPlaylistUri, 0, C.LENGTH_UNBOUNDED, null, + DataSpec.FLAG_ALLOW_GZIP); return new MediaPlaylistChunk(variantIndex, upstreamDataSource, dataSpec, mediaPlaylistUri.toString()); } private EncryptionKeyChunk newEncryptionKeyChunk(Uri keyUri, String iv) { - DataSpec dataSpec = new DataSpec(keyUri, 0, C.LENGTH_UNBOUNDED, null); + DataSpec dataSpec = new DataSpec(keyUri, 0, C.LENGTH_UNBOUNDED, null, DataSpec.FLAG_ALLOW_GZIP); return new EncryptionKeyChunk(upstreamDataSource, dataSpec, iv); } diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/DataSourceStream.java b/library/src/main/java/com/google/android/exoplayer/upstream/DataSourceStream.java index 5c4dcd65b2..33519d4e6c 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/DataSourceStream.java +++ b/library/src/main/java/com/google/android/exoplayer/upstream/DataSourceStream.java @@ -230,7 +230,7 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream long remainingLength = resolvedLength != C.LENGTH_UNBOUNDED ? resolvedLength - loadPosition : C.LENGTH_UNBOUNDED; loadDataSpec = new DataSpec(dataSpec.uri, dataSpec.position + loadPosition, - remainingLength, dataSpec.key); + remainingLength, dataSpec.key, dataSpec.flags); dataSource.open(loadDataSpec); } diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/DataSpec.java b/library/src/main/java/com/google/android/exoplayer/upstream/DataSpec.java index ff3b7dda0d..a153d955cb 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/DataSpec.java +++ b/library/src/main/java/com/google/android/exoplayer/upstream/DataSpec.java @@ -25,22 +25,32 @@ import android.net.Uri; */ public final class DataSpec { + /** + * Permits an underlying network stack to request that the server use gzip compression. + *
+ * Should not typically be set if the data being requested is already compressed (e.g. most audio + * and video requests). May be set when requesting other data. + *
+ * When a {@link DataSource} is used to request data with this flag set, and if the + * {@link DataSource} does make a network request, then the value returned from + * {@link DataSource#open(DataSpec)} will typically be {@link C#LENGTH_UNBOUNDED}. The data read + * from {@link DataSource#read(byte[], int, int)} will be the decompressed data. + */ + public static final int FLAG_ALLOW_GZIP = 1; + /** * Identifies the source from which data should be read. */ public final Uri uri; - /** - * True if the data at {@link #uri} is the full stream. False otherwise. An example where this - * may be false is if {@link #uri} defines the location of a cached part of the stream. - */ - public final boolean uriIsFullStream; /** * The absolute position of the data in the full stream. */ public final long absoluteStreamPosition; /** - * The position of the data when read from {@link #uri}. Always equal to - * {@link #absoluteStreamPosition} if {@link #uriIsFullStream}. + * The position of the data when read from {@link #uri}. + *
+ * Always equal to {@link #absoluteStreamPosition} unless the {@link #uri} defines the location
+ * of a subset of the underyling data.
*/
public final long position;
/**
@@ -52,6 +62,10 @@ public final class DataSpec {
* {@link DataSpec} is not intended to be used in conjunction with a cache.
*/
public final String key;
+ /**
+ * Request flags. Currently {@link #FLAG_ALLOW_GZIP} is the only supported flag.
+ */
+ public final int flags;
/**
* Construct a {@link DataSpec} for the given uri and with {@link #key} set to null.
@@ -59,11 +73,21 @@ public final class DataSpec {
* @param uri {@link #uri}.
*/
public DataSpec(Uri uri) {
- this(uri, 0, C.LENGTH_UNBOUNDED, null);
+ this(uri, 0);
}
/**
- * Construct a {@link DataSpec} for which {@link #uriIsFullStream} is true.
+ * Construct a {@link DataSpec} for the given uri and with {@link #key} set to null.
+ *
+ * @param uri {@link #uri}.
+ * @param flags {@link #flags}.
+ */
+ public DataSpec(Uri uri, int flags) {
+ this(uri, 0, C.LENGTH_UNBOUNDED, null, flags);
+ }
+
+ /**
+ * Construct a {@link DataSpec} where {@link #position} equals {@link #absoluteStreamPosition}.
*
* @param uri {@link #uri}.
* @param absoluteStreamPosition {@link #absoluteStreamPosition}, equal to {@link #position}.
@@ -71,50 +95,50 @@ public final class DataSpec {
* @param key {@link #key}.
*/
public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key) {
- this(uri, absoluteStreamPosition, length, key, absoluteStreamPosition, true);
+ this(uri, absoluteStreamPosition, absoluteStreamPosition, length, key, 0);
}
/**
- * Construct a {@link DataSpec} for which {@link #uriIsFullStream} is false.
+ * Construct a {@link DataSpec} where {@link #position} equals {@link #absoluteStreamPosition}.
*
* @param uri {@link #uri}.
- * @param absoluteStreamPosition {@link #absoluteStreamPosition}.
+ * @param absoluteStreamPosition {@link #absoluteStreamPosition}, equal to {@link #position}.
* @param length {@link #length}.
* @param key {@link #key}.
- * @param position {@link #position}.
+ * @param flags {@link #flags}.
*/
- public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key, long position) {
- this(uri, absoluteStreamPosition, length, key, position, false);
+ public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key, int flags) {
+ this(uri, absoluteStreamPosition, absoluteStreamPosition, length, key, flags);
}
/**
- * Construct a {@link DataSpec}.
+ * Construct a {@link DataSpec} where {@link #position} may differ from
+ * {@link #absoluteStreamPosition}.
*
* @param uri {@link #uri}.
* @param absoluteStreamPosition {@link #absoluteStreamPosition}.
+ * @param position {@link #position}.
* @param length {@link #length}.
* @param key {@link #key}.
- * @param position {@link #position}.
- * @param uriIsFullStream {@link #uriIsFullStream}.
+ * @param flags {@link #flags}.
*/
- public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key, long position,
- boolean uriIsFullStream) {
+ public DataSpec(Uri uri, long absoluteStreamPosition, long position, long length, String key,
+ int flags) {
Assertions.checkArgument(absoluteStreamPosition >= 0);
Assertions.checkArgument(position >= 0);
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNBOUNDED);
- Assertions.checkArgument(absoluteStreamPosition == position || !uriIsFullStream);
this.uri = uri;
- this.uriIsFullStream = uriIsFullStream;
this.absoluteStreamPosition = absoluteStreamPosition;
this.position = position;
this.length = length;
this.key = key;
+ this.flags = flags;
}
@Override
public String toString() {
- return "DataSpec[" + uri + ", " + uriIsFullStream + ", " + absoluteStreamPosition + ", " +
- position + ", " + length + ", " + key + "]";
+ return "DataSpec[" + uri + ", " + ", " + absoluteStreamPosition + ", " +
+ position + ", " + length + ", " + key + ", " + flags + "]";
}
}
diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/DefaultHttpDataSource.java b/library/src/main/java/com/google/android/exoplayer/upstream/DefaultHttpDataSource.java
index 6fbab8982e..8e0a4b75ec 100644
--- a/library/src/main/java/com/google/android/exoplayer/upstream/DefaultHttpDataSource.java
+++ b/library/src/main/java/com/google/android/exoplayer/upstream/DefaultHttpDataSource.java
@@ -132,19 +132,6 @@ public class DefaultHttpDataSource implements HttpDataSource {
}
}
- /*
- * TODO: If the server uses gzip compression when serving the response, this may end up returning
- * the size of the compressed response, where-as it should be returning the decompressed size or
- * -1. See: developer.android.com/reference/java/net/HttpURLConnection.html
- *
- * To fix this we should:
- *
- * 1. Explicitly require no compression for media requests (since media should be compressed
- * already) by setting the Accept-Encoding header to "identity"
- * 2. In other cases, for example when requesting manifests, we don't want to disable compression.
- * For these cases we should ensure that we return -1 here (and avoid performing any sanity
- * checks on the content length).
- */
@Override
public long open(DataSpec dataSpec) throws HttpDataSourceException {
this.dataSpec = dataSpec;
@@ -177,16 +164,23 @@ public class DefaultHttpDataSource implements HttpDataSource {
throw new InvalidContentTypeException(contentType, dataSpec);
}
- long contentLength = getContentLength(connection);
- dataLength = dataSpec.length == C.LENGTH_UNBOUNDED ? contentLength : dataSpec.length;
-
- if (dataSpec.length != C.LENGTH_UNBOUNDED && contentLength != C.LENGTH_UNBOUNDED
- && contentLength != dataSpec.length) {
- // The DataSpec specified a length and we resolved a length from the response headers, but
- // the two lengths do not match.
- closeConnection();
- throw new HttpDataSourceException(
- new UnexpectedLengthException(dataSpec.length, contentLength), dataSpec);
+ if ((dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) == 0) {
+ long contentLength = getContentLength(connection);
+ dataLength = dataSpec.length == C.LENGTH_UNBOUNDED ? contentLength : dataSpec.length;
+ if (dataSpec.length != C.LENGTH_UNBOUNDED && contentLength != C.LENGTH_UNBOUNDED
+ && contentLength != dataSpec.length) {
+ // The DataSpec specified a length and we resolved a length from the response headers, but
+ // the two lengths do not match.
+ closeConnection();
+ throw new HttpDataSourceException(
+ new UnexpectedLengthException(dataSpec.length, contentLength), dataSpec);
+ }
+ } else {
+ // Gzip is enabled. If the server opts to use gzip then the content length in the response
+ // will be that of the compressed data, which isn't what we want. Furthermore, there isn't a
+ // reliable way to determine whether the gzip was used or not. Hence we always treat the
+ // length as unknown.
+ dataLength = C.LENGTH_UNBOUNDED;
}
try {
@@ -301,6 +295,9 @@ public class DefaultHttpDataSource implements HttpDataSource {
}
setRangeHeader(connection, dataSpec);
connection.setRequestProperty("User-Agent", userAgent);
+ if ((dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) == 0) {
+ connection.setRequestProperty("Accept-Encoding", "identity");
+ }
connection.connect();
return connection;
}
diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/NetworkLoadable.java b/library/src/main/java/com/google/android/exoplayer/upstream/NetworkLoadable.java
index 5e8058f6dd..d4f4e6abf7 100644
--- a/library/src/main/java/com/google/android/exoplayer/upstream/NetworkLoadable.java
+++ b/library/src/main/java/com/google/android/exoplayer/upstream/NetworkLoadable.java
@@ -63,7 +63,7 @@ public final class NetworkLoadable