mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add flags to DataSpec. Support GZIP option.
Also remove uriIsFullStream. It's not doing anything particularly useful, so I think it makes sense to remove it from the public API; it's unlikely anyone is using it. Issue: #329
This commit is contained in:
parent
3868b1d4cb
commit
0c6566bce7
7 changed files with 80 additions and 58 deletions
|
|
@ -438,13 +438,14 @@ public class HlsChunkSource {
|
||||||
|
|
||||||
private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) {
|
private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex) {
|
||||||
Uri mediaPlaylistUri = Util.getMergedUri(baseUri, enabledVariants[variantIndex].url);
|
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,
|
return new MediaPlaylistChunk(variantIndex, upstreamDataSource, dataSpec,
|
||||||
mediaPlaylistUri.toString());
|
mediaPlaylistUri.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private EncryptionKeyChunk newEncryptionKeyChunk(Uri keyUri, String iv) {
|
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);
|
return new EncryptionKeyChunk(upstreamDataSource, dataSpec, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
|
||||||
long remainingLength = resolvedLength != C.LENGTH_UNBOUNDED
|
long remainingLength = resolvedLength != C.LENGTH_UNBOUNDED
|
||||||
? resolvedLength - loadPosition : C.LENGTH_UNBOUNDED;
|
? resolvedLength - loadPosition : C.LENGTH_UNBOUNDED;
|
||||||
loadDataSpec = new DataSpec(dataSpec.uri, dataSpec.position + loadPosition,
|
loadDataSpec = new DataSpec(dataSpec.uri, dataSpec.position + loadPosition,
|
||||||
remainingLength, dataSpec.key);
|
remainingLength, dataSpec.key, dataSpec.flags);
|
||||||
dataSource.open(loadDataSpec);
|
dataSource.open(loadDataSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,22 +25,32 @@ import android.net.Uri;
|
||||||
*/
|
*/
|
||||||
public final class DataSpec {
|
public final class DataSpec {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permits an underlying network stack to request that the server use gzip compression.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
* Identifies the source from which data should be read.
|
||||||
*/
|
*/
|
||||||
public final Uri uri;
|
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.
|
* The absolute position of the data in the full stream.
|
||||||
*/
|
*/
|
||||||
public final long absoluteStreamPosition;
|
public final long absoluteStreamPosition;
|
||||||
/**
|
/**
|
||||||
* The position of the data when read from {@link #uri}. Always equal to
|
* The position of the data when read from {@link #uri}.
|
||||||
* {@link #absoluteStreamPosition} if {@link #uriIsFullStream}.
|
* <p>
|
||||||
|
* Always equal to {@link #absoluteStreamPosition} unless the {@link #uri} defines the location
|
||||||
|
* of a subset of the underyling data.
|
||||||
*/
|
*/
|
||||||
public final long position;
|
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.
|
* {@link DataSpec} is not intended to be used in conjunction with a cache.
|
||||||
*/
|
*/
|
||||||
public final String key;
|
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.
|
* 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}.
|
* @param uri {@link #uri}.
|
||||||
*/
|
*/
|
||||||
public DataSpec(Uri 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 uri {@link #uri}.
|
||||||
* @param absoluteStreamPosition {@link #absoluteStreamPosition}, equal to {@link #position}.
|
* @param absoluteStreamPosition {@link #absoluteStreamPosition}, equal to {@link #position}.
|
||||||
|
|
@ -71,50 +95,50 @@ public final class DataSpec {
|
||||||
* @param key {@link #key}.
|
* @param key {@link #key}.
|
||||||
*/
|
*/
|
||||||
public DataSpec(Uri uri, long absoluteStreamPosition, long length, String 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 uri {@link #uri}.
|
||||||
* @param absoluteStreamPosition {@link #absoluteStreamPosition}.
|
* @param absoluteStreamPosition {@link #absoluteStreamPosition}, equal to {@link #position}.
|
||||||
* @param length {@link #length}.
|
* @param length {@link #length}.
|
||||||
* @param key {@link #key}.
|
* @param key {@link #key}.
|
||||||
* @param position {@link #position}.
|
* @param flags {@link #flags}.
|
||||||
*/
|
*/
|
||||||
public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key, long position) {
|
public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key, int flags) {
|
||||||
this(uri, absoluteStreamPosition, length, key, position, false);
|
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 uri {@link #uri}.
|
||||||
* @param absoluteStreamPosition {@link #absoluteStreamPosition}.
|
* @param absoluteStreamPosition {@link #absoluteStreamPosition}.
|
||||||
|
* @param position {@link #position}.
|
||||||
* @param length {@link #length}.
|
* @param length {@link #length}.
|
||||||
* @param key {@link #key}.
|
* @param key {@link #key}.
|
||||||
* @param position {@link #position}.
|
* @param flags {@link #flags}.
|
||||||
* @param uriIsFullStream {@link #uriIsFullStream}.
|
|
||||||
*/
|
*/
|
||||||
public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key, long position,
|
public DataSpec(Uri uri, long absoluteStreamPosition, long position, long length, String key,
|
||||||
boolean uriIsFullStream) {
|
int flags) {
|
||||||
Assertions.checkArgument(absoluteStreamPosition >= 0);
|
Assertions.checkArgument(absoluteStreamPosition >= 0);
|
||||||
Assertions.checkArgument(position >= 0);
|
Assertions.checkArgument(position >= 0);
|
||||||
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNBOUNDED);
|
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNBOUNDED);
|
||||||
Assertions.checkArgument(absoluteStreamPosition == position || !uriIsFullStream);
|
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.uriIsFullStream = uriIsFullStream;
|
|
||||||
this.absoluteStreamPosition = absoluteStreamPosition;
|
this.absoluteStreamPosition = absoluteStreamPosition;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
this.flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DataSpec[" + uri + ", " + uriIsFullStream + ", " + absoluteStreamPosition + ", " +
|
return "DataSpec[" + uri + ", " + ", " + absoluteStreamPosition + ", " +
|
||||||
position + ", " + length + ", " + key + "]";
|
position + ", " + length + ", " + key + ", " + flags + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
||||||
this.dataSpec = dataSpec;
|
this.dataSpec = dataSpec;
|
||||||
|
|
@ -177,16 +164,23 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
throw new InvalidContentTypeException(contentType, dataSpec);
|
throw new InvalidContentTypeException(contentType, dataSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
long contentLength = getContentLength(connection);
|
if ((dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) == 0) {
|
||||||
dataLength = dataSpec.length == C.LENGTH_UNBOUNDED ? contentLength : dataSpec.length;
|
long contentLength = getContentLength(connection);
|
||||||
|
dataLength = dataSpec.length == C.LENGTH_UNBOUNDED ? contentLength : dataSpec.length;
|
||||||
if (dataSpec.length != C.LENGTH_UNBOUNDED && contentLength != C.LENGTH_UNBOUNDED
|
if (dataSpec.length != C.LENGTH_UNBOUNDED && contentLength != C.LENGTH_UNBOUNDED
|
||||||
&& contentLength != dataSpec.length) {
|
&& contentLength != dataSpec.length) {
|
||||||
// The DataSpec specified a length and we resolved a length from the response headers, but
|
// The DataSpec specified a length and we resolved a length from the response headers, but
|
||||||
// the two lengths do not match.
|
// the two lengths do not match.
|
||||||
closeConnection();
|
closeConnection();
|
||||||
throw new HttpDataSourceException(
|
throw new HttpDataSourceException(
|
||||||
new UnexpectedLengthException(dataSpec.length, contentLength), dataSpec);
|
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 {
|
try {
|
||||||
|
|
@ -301,6 +295,9 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
}
|
}
|
||||||
setRangeHeader(connection, dataSpec);
|
setRangeHeader(connection, dataSpec);
|
||||||
connection.setRequestProperty("User-Agent", userAgent);
|
connection.setRequestProperty("User-Agent", userAgent);
|
||||||
|
if ((dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) == 0) {
|
||||||
|
connection.setRequestProperty("Accept-Encoding", "identity");
|
||||||
|
}
|
||||||
connection.connect();
|
connection.connect();
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ public final class NetworkLoadable<T> implements Loadable {
|
||||||
public NetworkLoadable(String url, HttpDataSource httpDataSource, Parser<T> parser) {
|
public NetworkLoadable(String url, HttpDataSource httpDataSource, Parser<T> parser) {
|
||||||
this.httpDataSource = httpDataSource;
|
this.httpDataSource = httpDataSource;
|
||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
dataSpec = new DataSpec(Uri.parse(url));
|
dataSpec = new DataSpec(Uri.parse(url), DataSpec.FLAG_ALLOW_GZIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ public final class TeeDataSource implements DataSource {
|
||||||
long dataLength = upstream.open(dataSpec);
|
long dataLength = upstream.open(dataSpec);
|
||||||
if (dataSpec.length == C.LENGTH_UNBOUNDED && dataLength != C.LENGTH_UNBOUNDED) {
|
if (dataSpec.length == C.LENGTH_UNBOUNDED && dataLength != C.LENGTH_UNBOUNDED) {
|
||||||
// Reconstruct dataSpec in order to provide the resolved length to the sink.
|
// Reconstruct dataSpec in order to provide the resolved length to the sink.
|
||||||
dataSpec = new DataSpec(dataSpec.uri, dataSpec.absoluteStreamPosition, dataLength,
|
dataSpec = new DataSpec(dataSpec.uri, dataSpec.absoluteStreamPosition, dataSpec.position,
|
||||||
dataSpec.key, dataSpec.position, dataSpec.uriIsFullStream);
|
dataLength, dataSpec.key, dataSpec.flags);
|
||||||
}
|
}
|
||||||
dataSink.open(dataSpec);
|
dataSink.open(dataSpec);
|
||||||
return dataLength;
|
return dataLength;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import com.google.android.exoplayer.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer.upstream.FileDataSource;
|
import com.google.android.exoplayer.upstream.FileDataSource;
|
||||||
import com.google.android.exoplayer.upstream.TeeDataSource;
|
import com.google.android.exoplayer.upstream.TeeDataSource;
|
||||||
import com.google.android.exoplayer.upstream.cache.CacheDataSink.CacheDataSinkException;
|
import com.google.android.exoplayer.upstream.cache.CacheDataSink.CacheDataSinkException;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
@ -64,6 +63,7 @@ public final class CacheDataSource implements DataSource {
|
||||||
|
|
||||||
private DataSource currentDataSource;
|
private DataSource currentDataSource;
|
||||||
private Uri uri;
|
private Uri uri;
|
||||||
|
private int flags;
|
||||||
private String key;
|
private String key;
|
||||||
private long readPosition;
|
private long readPosition;
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
|
|
@ -125,9 +125,9 @@ public final class CacheDataSource implements DataSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws IOException {
|
public long open(DataSpec dataSpec) throws IOException {
|
||||||
Assertions.checkState(dataSpec.uriIsFullStream);
|
|
||||||
try {
|
try {
|
||||||
uri = dataSpec.uri;
|
uri = dataSpec.uri;
|
||||||
|
flags = dataSpec.flags;
|
||||||
key = dataSpec.key;
|
key = dataSpec.key;
|
||||||
readPosition = dataSpec.position;
|
readPosition = dataSpec.position;
|
||||||
bytesRemaining = dataSpec.length;
|
bytesRemaining = dataSpec.length;
|
||||||
|
|
@ -201,19 +201,19 @@ public final class CacheDataSource implements DataSource {
|
||||||
// The data is locked in the cache, or we're ignoring the cache. Bypass the cache and read
|
// The data is locked in the cache, or we're ignoring the cache. Bypass the cache and read
|
||||||
// from upstream.
|
// from upstream.
|
||||||
currentDataSource = upstreamDataSource;
|
currentDataSource = upstreamDataSource;
|
||||||
dataSpec = new DataSpec(uri, readPosition, bytesRemaining, key);
|
dataSpec = new DataSpec(uri, readPosition, bytesRemaining, key, flags);
|
||||||
} else if (span.isCached) {
|
} else if (span.isCached) {
|
||||||
// Data is cached, read from cache.
|
// Data is cached, read from cache.
|
||||||
Uri fileUri = Uri.fromFile(span.file);
|
Uri fileUri = Uri.fromFile(span.file);
|
||||||
long filePosition = readPosition - span.position;
|
long filePosition = readPosition - span.position;
|
||||||
long length = Math.min(span.length - filePosition, bytesRemaining);
|
long length = Math.min(span.length - filePosition, bytesRemaining);
|
||||||
dataSpec = new DataSpec(fileUri, readPosition, length, key, filePosition);
|
dataSpec = new DataSpec(fileUri, readPosition, filePosition, length, key, flags);
|
||||||
currentDataSource = cacheReadDataSource;
|
currentDataSource = cacheReadDataSource;
|
||||||
} else {
|
} else {
|
||||||
// Data is not cached, and data is not locked, read from upstream with cache backing.
|
// Data is not cached, and data is not locked, read from upstream with cache backing.
|
||||||
lockedSpan = span;
|
lockedSpan = span;
|
||||||
long length = span.isOpenEnded() ? bytesRemaining : Math.min(span.length, bytesRemaining);
|
long length = span.isOpenEnded() ? bytesRemaining : Math.min(span.length, bytesRemaining);
|
||||||
dataSpec = new DataSpec(uri, readPosition, length, key);
|
dataSpec = new DataSpec(uri, readPosition, length, key, flags);
|
||||||
currentDataSource = cacheWriteDataSource != null ? cacheWriteDataSource
|
currentDataSource = cacheWriteDataSource != null ? cacheWriteDataSource
|
||||||
: upstreamDataSource;
|
: upstreamDataSource;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue