mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Use BaseDataSource for all internal leaf data sources.
This allows all leaf data sources (i.e. ones which do not forward the requests to other data sources) to accept multiple listeners. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=203097587
This commit is contained in:
parent
e48b712a32
commit
985160a47d
15 changed files with 486 additions and 459 deletions
|
|
@ -16,10 +16,13 @@
|
||||||
package com.google.android.exoplayer2.ext.cronet;
|
package com.google.android.exoplayer2.ext.cronet;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
||||||
|
import com.google.android.exoplayer2.upstream.BaseDataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSourceException;
|
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
|
|
@ -48,9 +51,10 @@ import org.chromium.net.UrlResponseInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DataSource without intermediate buffer based on Cronet API set using UrlRequest.
|
* DataSource without intermediate buffer based on Cronet API set using UrlRequest.
|
||||||
|
*
|
||||||
* <p>This class's methods are organized in the sequence of expected calls.
|
* <p>This class's methods are organized in the sequence of expected calls.
|
||||||
*/
|
*/
|
||||||
public class CronetDataSource extends UrlRequest.Callback implements HttpDataSource {
|
public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when an error is encountered when trying to open a {@link CronetDataSource}.
|
* Thrown when an error is encountered when trying to open a {@link CronetDataSource}.
|
||||||
|
|
@ -96,6 +100,8 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000;
|
public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000;
|
||||||
|
|
||||||
|
/* package */ final UrlRequest.Callback urlRequestCallback;
|
||||||
|
|
||||||
private static final String TAG = "CronetDataSource";
|
private static final String TAG = "CronetDataSource";
|
||||||
private static final String CONTENT_TYPE = "Content-Type";
|
private static final String CONTENT_TYPE = "Content-Type";
|
||||||
private static final String SET_COOKIE = "Set-Cookie";
|
private static final String SET_COOKIE = "Set-Cookie";
|
||||||
|
|
@ -109,7 +115,6 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
private final CronetEngine cronetEngine;
|
private final CronetEngine cronetEngine;
|
||||||
private final Executor executor;
|
private final Executor executor;
|
||||||
private final Predicate<String> contentTypePredicate;
|
private final Predicate<String> contentTypePredicate;
|
||||||
private final TransferListener<? super CronetDataSource> listener;
|
|
||||||
private final int connectTimeoutMs;
|
private final int connectTimeoutMs;
|
||||||
private final int readTimeoutMs;
|
private final int readTimeoutMs;
|
||||||
private final boolean resetTimeoutOnRedirects;
|
private final boolean resetTimeoutOnRedirects;
|
||||||
|
|
@ -144,41 +149,49 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cronetEngine A CronetEngine.
|
* @param cronetEngine A CronetEngine.
|
||||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses.
|
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
|
||||||
* This may be a direct executor (i.e. executes tasks on the calling thread) in order
|
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
|
||||||
* to avoid a thread hop from Cronet's internal network thread to the response handling
|
* hop from Cronet's internal network thread to the response handling thread. However, to
|
||||||
* thread. However, to avoid slowing down overall network performance, care must be taken
|
* avoid slowing down overall network performance, care must be taken to make sure response
|
||||||
* to make sure response handling is a fast operation when using a direct executor.
|
* handling is a fast operation when using a direct executor.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then an {@link InvalidContentTypeException} is thrown from
|
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
*/
|
*/
|
||||||
public CronetDataSource(CronetEngine cronetEngine, Executor executor,
|
public CronetDataSource(
|
||||||
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener) {
|
CronetEngine cronetEngine,
|
||||||
|
Executor executor,
|
||||||
|
Predicate<String> contentTypePredicate,
|
||||||
|
@Nullable TransferListener<? super DataSource> listener) {
|
||||||
this(cronetEngine, executor, contentTypePredicate, listener, DEFAULT_CONNECT_TIMEOUT_MILLIS,
|
this(cronetEngine, executor, contentTypePredicate, listener, DEFAULT_CONNECT_TIMEOUT_MILLIS,
|
||||||
DEFAULT_READ_TIMEOUT_MILLIS, false, null, false);
|
DEFAULT_READ_TIMEOUT_MILLIS, false, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cronetEngine A CronetEngine.
|
* @param cronetEngine A CronetEngine.
|
||||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses.
|
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
|
||||||
* This may be a direct executor (i.e. executes tasks on the calling thread) in order
|
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
|
||||||
* to avoid a thread hop from Cronet's internal network thread to the response handling
|
* hop from Cronet's internal network thread to the response handling thread. However, to
|
||||||
* thread. However, to avoid slowing down overall network performance, care must be taken
|
* avoid slowing down overall network performance, care must be taken to make sure response
|
||||||
* to make sure response handling is a fast operation when using a direct executor.
|
* handling is a fast operation when using a direct executor.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then an {@link InvalidContentTypeException} is thrown from
|
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
* @param connectTimeoutMs The connection timeout, in milliseconds.
|
* @param connectTimeoutMs The connection timeout, in milliseconds.
|
||||||
* @param readTimeoutMs The read timeout, in milliseconds.
|
* @param readTimeoutMs The read timeout, in milliseconds.
|
||||||
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
|
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
|
||||||
* @param defaultRequestProperties The default request properties to be used.
|
* @param defaultRequestProperties The default request properties to be used.
|
||||||
*/
|
*/
|
||||||
public CronetDataSource(CronetEngine cronetEngine, Executor executor,
|
public CronetDataSource(
|
||||||
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener,
|
CronetEngine cronetEngine,
|
||||||
int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects,
|
Executor executor,
|
||||||
|
Predicate<String> contentTypePredicate,
|
||||||
|
@Nullable TransferListener<? super DataSource> listener,
|
||||||
|
int connectTimeoutMs,
|
||||||
|
int readTimeoutMs,
|
||||||
|
boolean resetTimeoutOnRedirects,
|
||||||
RequestProperties defaultRequestProperties) {
|
RequestProperties defaultRequestProperties) {
|
||||||
this(cronetEngine, executor, contentTypePredicate, listener, connectTimeoutMs,
|
this(cronetEngine, executor, contentTypePredicate, listener, connectTimeoutMs,
|
||||||
readTimeoutMs, resetTimeoutOnRedirects, Clock.DEFAULT, defaultRequestProperties, false);
|
readTimeoutMs, resetTimeoutOnRedirects, Clock.DEFAULT, defaultRequestProperties, false);
|
||||||
|
|
@ -186,14 +199,14 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cronetEngine A CronetEngine.
|
* @param cronetEngine A CronetEngine.
|
||||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses.
|
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
|
||||||
* This may be a direct executor (i.e. executes tasks on the calling thread) in order
|
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
|
||||||
* to avoid a thread hop from Cronet's internal network thread to the response handling
|
* hop from Cronet's internal network thread to the response handling thread. However, to
|
||||||
* thread. However, to avoid slowing down overall network performance, care must be taken
|
* avoid slowing down overall network performance, care must be taken to make sure response
|
||||||
* to make sure response handling is a fast operation when using a direct executor.
|
* handling is a fast operation when using a direct executor.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then an {@link InvalidContentTypeException} is thrown from
|
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
* @param connectTimeoutMs The connection timeout, in milliseconds.
|
* @param connectTimeoutMs The connection timeout, in milliseconds.
|
||||||
* @param readTimeoutMs The read timeout, in milliseconds.
|
* @param readTimeoutMs The read timeout, in milliseconds.
|
||||||
|
|
@ -202,23 +215,37 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
* @param handleSetCookieRequests Whether "Set-Cookie" requests on redirect should be forwarded to
|
* @param handleSetCookieRequests Whether "Set-Cookie" requests on redirect should be forwarded to
|
||||||
* the redirect url in the "Cookie" header.
|
* the redirect url in the "Cookie" header.
|
||||||
*/
|
*/
|
||||||
public CronetDataSource(CronetEngine cronetEngine, Executor executor,
|
public CronetDataSource(
|
||||||
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener,
|
CronetEngine cronetEngine,
|
||||||
int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects,
|
Executor executor,
|
||||||
RequestProperties defaultRequestProperties, boolean handleSetCookieRequests) {
|
Predicate<String> contentTypePredicate,
|
||||||
|
@Nullable TransferListener<? super DataSource> listener,
|
||||||
|
int connectTimeoutMs,
|
||||||
|
int readTimeoutMs,
|
||||||
|
boolean resetTimeoutOnRedirects,
|
||||||
|
RequestProperties defaultRequestProperties,
|
||||||
|
boolean handleSetCookieRequests) {
|
||||||
this(cronetEngine, executor, contentTypePredicate, listener, connectTimeoutMs,
|
this(cronetEngine, executor, contentTypePredicate, listener, connectTimeoutMs,
|
||||||
readTimeoutMs, resetTimeoutOnRedirects, Clock.DEFAULT, defaultRequestProperties,
|
readTimeoutMs, resetTimeoutOnRedirects, Clock.DEFAULT, defaultRequestProperties,
|
||||||
handleSetCookieRequests);
|
handleSetCookieRequests);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ CronetDataSource(CronetEngine cronetEngine, Executor executor,
|
/* package */ CronetDataSource(
|
||||||
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener,
|
CronetEngine cronetEngine,
|
||||||
int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects, Clock clock,
|
Executor executor,
|
||||||
RequestProperties defaultRequestProperties, boolean handleSetCookieRequests) {
|
Predicate<String> contentTypePredicate,
|
||||||
|
@Nullable TransferListener<? super DataSource> listener,
|
||||||
|
int connectTimeoutMs,
|
||||||
|
int readTimeoutMs,
|
||||||
|
boolean resetTimeoutOnRedirects,
|
||||||
|
Clock clock,
|
||||||
|
RequestProperties defaultRequestProperties,
|
||||||
|
boolean handleSetCookieRequests) {
|
||||||
|
super(DataSource.TYPE_REMOTE);
|
||||||
|
this.urlRequestCallback = new UrlRequestCallback();
|
||||||
this.cronetEngine = Assertions.checkNotNull(cronetEngine);
|
this.cronetEngine = Assertions.checkNotNull(cronetEngine);
|
||||||
this.executor = Assertions.checkNotNull(executor);
|
this.executor = Assertions.checkNotNull(executor);
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
this.listener = listener;
|
|
||||||
this.connectTimeoutMs = connectTimeoutMs;
|
this.connectTimeoutMs = connectTimeoutMs;
|
||||||
this.readTimeoutMs = readTimeoutMs;
|
this.readTimeoutMs = readTimeoutMs;
|
||||||
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
|
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
|
||||||
|
|
@ -227,6 +254,9 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
this.handleSetCookieRequests = handleSetCookieRequests;
|
this.handleSetCookieRequests = handleSetCookieRequests;
|
||||||
requestProperties = new RequestProperties();
|
requestProperties = new RequestProperties();
|
||||||
operation = new ConditionVariable();
|
operation = new ConditionVariable();
|
||||||
|
if (listener != null) {
|
||||||
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HttpDataSource implementation.
|
// HttpDataSource implementation.
|
||||||
|
|
@ -324,9 +354,7 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesRemaining;
|
return bytesRemaining;
|
||||||
}
|
}
|
||||||
|
|
@ -392,9 +420,7 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
if (bytesRemaining != C.LENGTH_UNSET) {
|
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||||
bytesRemaining -= bytesRead;
|
bytesRemaining -= bytesRead;
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
bytesTransferred(bytesRead);
|
||||||
listener.onBytesTransferred(this, bytesRead);
|
|
||||||
}
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,107 +439,17 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
finished = false;
|
finished = false;
|
||||||
if (opened) {
|
if (opened) {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UrlRequest.Callback implementation
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onRedirectReceived(UrlRequest request, UrlResponseInfo info,
|
|
||||||
String newLocationUrl) {
|
|
||||||
if (request != currentUrlRequest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (currentDataSpec.postBody != null) {
|
|
||||||
int responseCode = info.getHttpStatusCode();
|
|
||||||
// The industry standard is to disregard POST redirects when the status code is 307 or 308.
|
|
||||||
// For other redirect response codes the POST request is converted to a GET request and the
|
|
||||||
// redirect is followed.
|
|
||||||
if (responseCode == 307 || responseCode == 308) {
|
|
||||||
exception = new InvalidResponseCodeException(responseCode, info.getAllHeaders(),
|
|
||||||
currentDataSpec);
|
|
||||||
operation.open();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (resetTimeoutOnRedirects) {
|
|
||||||
resetConnectTimeout();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, List<String>> headers = info.getAllHeaders();
|
|
||||||
if (!handleSetCookieRequests || isEmpty(headers.get(SET_COOKIE))) {
|
|
||||||
request.followRedirect();
|
|
||||||
} else {
|
|
||||||
currentUrlRequest.cancel();
|
|
||||||
DataSpec redirectUrlDataSpec = new DataSpec(Uri.parse(newLocationUrl),
|
|
||||||
currentDataSpec.postBody, currentDataSpec.absoluteStreamPosition,
|
|
||||||
currentDataSpec.position, currentDataSpec.length, currentDataSpec.key,
|
|
||||||
currentDataSpec.flags);
|
|
||||||
UrlRequest.Builder requestBuilder;
|
|
||||||
try {
|
|
||||||
requestBuilder = buildRequestBuilder(redirectUrlDataSpec);
|
|
||||||
} catch (IOException e) {
|
|
||||||
exception = e;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String cookieHeadersValue = parseCookies(headers.get(SET_COOKIE));
|
|
||||||
attachCookies(requestBuilder, cookieHeadersValue);
|
|
||||||
currentUrlRequest = requestBuilder.build();
|
|
||||||
currentUrlRequest.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
|
|
||||||
if (request != currentUrlRequest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
responseInfo = info;
|
|
||||||
operation.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onReadCompleted(UrlRequest request, UrlResponseInfo info,
|
|
||||||
ByteBuffer buffer) {
|
|
||||||
if (request != currentUrlRequest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
operation.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onSucceeded(UrlRequest request, UrlResponseInfo info) {
|
|
||||||
if (request != currentUrlRequest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
finished = true;
|
|
||||||
operation.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onFailed(UrlRequest request, UrlResponseInfo info,
|
|
||||||
CronetException error) {
|
|
||||||
if (request != currentUrlRequest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (error instanceof NetworkException
|
|
||||||
&& ((NetworkException) error).getErrorCode()
|
|
||||||
== NetworkException.ERROR_HOSTNAME_NOT_RESOLVED) {
|
|
||||||
exception = new UnknownHostException();
|
|
||||||
} else {
|
|
||||||
exception = error;
|
|
||||||
}
|
|
||||||
operation.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
private UrlRequest.Builder buildRequestBuilder(DataSpec dataSpec) throws IOException {
|
private UrlRequest.Builder buildRequestBuilder(DataSpec dataSpec) throws IOException {
|
||||||
UrlRequest.Builder requestBuilder = cronetEngine.newUrlRequestBuilder(
|
UrlRequest.Builder requestBuilder =
|
||||||
dataSpec.uri.toString(), this, executor).allowDirectExecutor();
|
cronetEngine
|
||||||
|
.newUrlRequestBuilder(dataSpec.uri.toString(), urlRequestCallback, executor)
|
||||||
|
.allowDirectExecutor();
|
||||||
// Set the headers.
|
// Set the headers.
|
||||||
boolean isContentTypeHeaderSet = false;
|
boolean isContentTypeHeaderSet = false;
|
||||||
if (defaultRequestProperties != null) {
|
if (defaultRequestProperties != null) {
|
||||||
|
|
@ -656,4 +592,99 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||||
return list == null || list.isEmpty();
|
return list == null || list.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class UrlRequestCallback extends UrlRequest.Callback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onRedirectReceived(
|
||||||
|
UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
|
||||||
|
if (request != currentUrlRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentDataSpec.postBody != null) {
|
||||||
|
int responseCode = info.getHttpStatusCode();
|
||||||
|
// The industry standard is to disregard POST redirects when the status code is 307 or 308.
|
||||||
|
// For other redirect response codes the POST request is converted to a GET request and the
|
||||||
|
// redirect is followed.
|
||||||
|
if (responseCode == 307 || responseCode == 308) {
|
||||||
|
exception =
|
||||||
|
new InvalidResponseCodeException(responseCode, info.getAllHeaders(), currentDataSpec);
|
||||||
|
operation.open();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resetTimeoutOnRedirects) {
|
||||||
|
resetConnectTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<String>> headers = info.getAllHeaders();
|
||||||
|
if (!handleSetCookieRequests || isEmpty(headers.get(SET_COOKIE))) {
|
||||||
|
request.followRedirect();
|
||||||
|
} else {
|
||||||
|
currentUrlRequest.cancel();
|
||||||
|
DataSpec redirectUrlDataSpec =
|
||||||
|
new DataSpec(
|
||||||
|
Uri.parse(newLocationUrl),
|
||||||
|
currentDataSpec.postBody,
|
||||||
|
currentDataSpec.absoluteStreamPosition,
|
||||||
|
currentDataSpec.position,
|
||||||
|
currentDataSpec.length,
|
||||||
|
currentDataSpec.key,
|
||||||
|
currentDataSpec.flags);
|
||||||
|
UrlRequest.Builder requestBuilder;
|
||||||
|
try {
|
||||||
|
requestBuilder = buildRequestBuilder(redirectUrlDataSpec);
|
||||||
|
} catch (IOException e) {
|
||||||
|
exception = e;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String cookieHeadersValue = parseCookies(headers.get(SET_COOKIE));
|
||||||
|
attachCookies(requestBuilder, cookieHeadersValue);
|
||||||
|
currentUrlRequest = requestBuilder.build();
|
||||||
|
currentUrlRequest.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
|
||||||
|
if (request != currentUrlRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
responseInfo = info;
|
||||||
|
operation.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onReadCompleted(
|
||||||
|
UrlRequest request, UrlResponseInfo info, ByteBuffer buffer) {
|
||||||
|
if (request != currentUrlRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
operation.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onSucceeded(UrlRequest request, UrlResponseInfo info) {
|
||||||
|
if (request != currentUrlRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finished = true;
|
||||||
|
operation.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onFailed(
|
||||||
|
UrlRequest request, UrlResponseInfo info, CronetException error) {
|
||||||
|
if (request != currentUrlRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (error instanceof NetworkException
|
||||||
|
&& ((NetworkException) error).getErrorCode()
|
||||||
|
== NetworkException.ERROR_HOSTNAME_NOT_RESOLVED) {
|
||||||
|
exception = new UnknownHostException();
|
||||||
|
} else {
|
||||||
|
exception = error;
|
||||||
|
}
|
||||||
|
operation.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
@ -33,6 +32,7 @@ import android.net.Uri;
|
||||||
import android.os.ConditionVariable;
|
import android.os.ConditionVariable;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
|
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
|
||||||
|
|
@ -87,7 +87,7 @@ public final class CronetDataSourceTest {
|
||||||
@Mock private UrlRequest.Builder mockUrlRequestBuilder;
|
@Mock private UrlRequest.Builder mockUrlRequestBuilder;
|
||||||
@Mock private UrlRequest mockUrlRequest;
|
@Mock private UrlRequest mockUrlRequest;
|
||||||
@Mock private Predicate<String> mockContentTypePredicate;
|
@Mock private Predicate<String> mockContentTypePredicate;
|
||||||
@Mock private TransferListener<CronetDataSource> mockTransferListener;
|
@Mock private TransferListener<DataSource> mockTransferListener;
|
||||||
@Mock private Executor mockExecutor;
|
@Mock private Executor mockExecutor;
|
||||||
@Mock private NetworkException mockNetworkException;
|
@Mock private NetworkException mockNetworkException;
|
||||||
@Mock private CronetEngine mockCronetEngine;
|
@Mock private CronetEngine mockCronetEngine;
|
||||||
|
|
@ -99,18 +99,17 @@ public final class CronetDataSourceTest {
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
dataSourceUnderTest =
|
dataSourceUnderTest =
|
||||||
spy(
|
new CronetDataSource(
|
||||||
new CronetDataSource(
|
mockCronetEngine,
|
||||||
mockCronetEngine,
|
mockExecutor,
|
||||||
mockExecutor,
|
mockContentTypePredicate,
|
||||||
mockContentTypePredicate,
|
mockTransferListener,
|
||||||
mockTransferListener,
|
TEST_CONNECT_TIMEOUT_MS,
|
||||||
TEST_CONNECT_TIMEOUT_MS,
|
TEST_READ_TIMEOUT_MS,
|
||||||
TEST_READ_TIMEOUT_MS,
|
true, // resetTimeoutOnRedirects
|
||||||
true, // resetTimeoutOnRedirects
|
Clock.DEFAULT,
|
||||||
Clock.DEFAULT,
|
null,
|
||||||
null,
|
false);
|
||||||
false));
|
|
||||||
when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true);
|
when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true);
|
||||||
when(mockCronetEngine.newUrlRequestBuilder(
|
when(mockCronetEngine.newUrlRequestBuilder(
|
||||||
anyString(), any(UrlRequest.Callback.class), any(Executor.class)))
|
anyString(), any(UrlRequest.Callback.class), any(Executor.class)))
|
||||||
|
|
@ -172,9 +171,10 @@ public final class CronetDataSourceTest {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
// Invoke the callback for the previous request.
|
// Invoke the callback for the previous request.
|
||||||
dataSourceUnderTest.onFailed(
|
dataSourceUnderTest.urlRequestCallback.onFailed(
|
||||||
mockUrlRequest, testUrlResponseInfo, mockNetworkException);
|
mockUrlRequest, testUrlResponseInfo, mockNetworkException);
|
||||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest2, testUrlResponseInfo);
|
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
||||||
|
mockUrlRequest2, testUrlResponseInfo);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -601,7 +601,7 @@ public final class CronetDataSourceTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectResponseBeforeTimeout() throws InterruptedException {
|
public void testConnectResponseBeforeTimeout() throws Exception {
|
||||||
long startTimeMs = SystemClock.elapsedRealtime();
|
long startTimeMs = SystemClock.elapsedRealtime();
|
||||||
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
||||||
final CountDownLatch openLatch = new CountDownLatch(1);
|
final CountDownLatch openLatch = new CountDownLatch(1);
|
||||||
|
|
@ -625,12 +625,12 @@ public final class CronetDataSourceTest {
|
||||||
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||||
assertNotCountedDown(openLatch);
|
assertNotCountedDown(openLatch);
|
||||||
// The response arrives just in time.
|
// The response arrives just in time.
|
||||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
dataSourceUnderTest.urlRequestCallback.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||||
openLatch.await();
|
openLatch.await();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRedirectIncreasesConnectionTimeout() throws InterruptedException {
|
public void testRedirectIncreasesConnectionTimeout() throws Exception {
|
||||||
long startTimeMs = SystemClock.elapsedRealtime();
|
long startTimeMs = SystemClock.elapsedRealtime();
|
||||||
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
||||||
final CountDownLatch timedOutLatch = new CountDownLatch(1);
|
final CountDownLatch timedOutLatch = new CountDownLatch(1);
|
||||||
|
|
@ -659,7 +659,7 @@ public final class CronetDataSourceTest {
|
||||||
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||||
assertNotCountedDown(timedOutLatch);
|
assertNotCountedDown(timedOutLatch);
|
||||||
// A redirect arrives just in time.
|
// A redirect arrives just in time.
|
||||||
dataSourceUnderTest.onRedirectReceived(
|
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
||||||
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl1");
|
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl1");
|
||||||
|
|
||||||
long newTimeoutMs = 2 * TEST_CONNECT_TIMEOUT_MS - 1;
|
long newTimeoutMs = 2 * TEST_CONNECT_TIMEOUT_MS - 1;
|
||||||
|
|
@ -667,7 +667,7 @@ public final class CronetDataSourceTest {
|
||||||
// We should still be trying to open as we approach the new timeout.
|
// We should still be trying to open as we approach the new timeout.
|
||||||
assertNotCountedDown(timedOutLatch);
|
assertNotCountedDown(timedOutLatch);
|
||||||
// A redirect arrives just in time.
|
// A redirect arrives just in time.
|
||||||
dataSourceUnderTest.onRedirectReceived(
|
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
||||||
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl2");
|
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl2");
|
||||||
|
|
||||||
newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2;
|
newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2;
|
||||||
|
|
@ -700,18 +700,17 @@ public final class CronetDataSourceTest {
|
||||||
testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders()
|
testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders()
|
||||||
throws HttpDataSourceException {
|
throws HttpDataSourceException {
|
||||||
dataSourceUnderTest =
|
dataSourceUnderTest =
|
||||||
spy(
|
new CronetDataSource(
|
||||||
new CronetDataSource(
|
mockCronetEngine,
|
||||||
mockCronetEngine,
|
mockExecutor,
|
||||||
mockExecutor,
|
mockContentTypePredicate,
|
||||||
mockContentTypePredicate,
|
mockTransferListener,
|
||||||
mockTransferListener,
|
TEST_CONNECT_TIMEOUT_MS,
|
||||||
TEST_CONNECT_TIMEOUT_MS,
|
TEST_READ_TIMEOUT_MS,
|
||||||
TEST_READ_TIMEOUT_MS,
|
true, // resetTimeoutOnRedirects
|
||||||
true, // resetTimeoutOnRedirects
|
Clock.DEFAULT,
|
||||||
Clock.DEFAULT,
|
null,
|
||||||
null,
|
true);
|
||||||
true));
|
|
||||||
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
||||||
|
|
||||||
mockSingleRedirectSuccess();
|
mockSingleRedirectSuccess();
|
||||||
|
|
@ -732,18 +731,17 @@ public final class CronetDataSourceTest {
|
||||||
throws HttpDataSourceException {
|
throws HttpDataSourceException {
|
||||||
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null);
|
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null);
|
||||||
dataSourceUnderTest =
|
dataSourceUnderTest =
|
||||||
spy(
|
new CronetDataSource(
|
||||||
new CronetDataSource(
|
mockCronetEngine,
|
||||||
mockCronetEngine,
|
mockExecutor,
|
||||||
mockExecutor,
|
mockContentTypePredicate,
|
||||||
mockContentTypePredicate,
|
mockTransferListener,
|
||||||
mockTransferListener,
|
TEST_CONNECT_TIMEOUT_MS,
|
||||||
TEST_CONNECT_TIMEOUT_MS,
|
TEST_READ_TIMEOUT_MS,
|
||||||
TEST_READ_TIMEOUT_MS,
|
true, // resetTimeoutOnRedirects
|
||||||
true, // resetTimeoutOnRedirects
|
Clock.DEFAULT,
|
||||||
Clock.DEFAULT,
|
null,
|
||||||
null,
|
true);
|
||||||
true));
|
|
||||||
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
||||||
|
|
||||||
mockSingleRedirectSuccess();
|
mockSingleRedirectSuccess();
|
||||||
|
|
@ -772,18 +770,17 @@ public final class CronetDataSourceTest {
|
||||||
public void testRedirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie()
|
public void testRedirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie()
|
||||||
throws HttpDataSourceException {
|
throws HttpDataSourceException {
|
||||||
dataSourceUnderTest =
|
dataSourceUnderTest =
|
||||||
spy(
|
new CronetDataSource(
|
||||||
new CronetDataSource(
|
mockCronetEngine,
|
||||||
mockCronetEngine,
|
mockExecutor,
|
||||||
mockExecutor,
|
mockContentTypePredicate,
|
||||||
mockContentTypePredicate,
|
mockTransferListener,
|
||||||
mockTransferListener,
|
TEST_CONNECT_TIMEOUT_MS,
|
||||||
TEST_CONNECT_TIMEOUT_MS,
|
TEST_READ_TIMEOUT_MS,
|
||||||
TEST_READ_TIMEOUT_MS,
|
true, // resetTimeoutOnRedirects
|
||||||
true, // resetTimeoutOnRedirects
|
Clock.DEFAULT,
|
||||||
Clock.DEFAULT,
|
null,
|
||||||
null,
|
true);
|
||||||
true));
|
|
||||||
mockSingleRedirectSuccess();
|
mockSingleRedirectSuccess();
|
||||||
mockFollowRedirectSuccess();
|
mockFollowRedirectSuccess();
|
||||||
|
|
||||||
|
|
@ -889,7 +886,8 @@ public final class CronetDataSourceTest {
|
||||||
new Answer<Object>() {
|
new Answer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
||||||
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -902,7 +900,7 @@ public final class CronetDataSourceTest {
|
||||||
new Answer<Object>() {
|
new Answer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
dataSourceUnderTest.onRedirectReceived(
|
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfo(307), // statusCode
|
createUrlResponseInfo(307), // statusCode
|
||||||
"http://redirect.location.com");
|
"http://redirect.location.com");
|
||||||
|
|
@ -920,12 +918,13 @@ public final class CronetDataSourceTest {
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
if (!redirectCalled) {
|
if (!redirectCalled) {
|
||||||
redirectCalled = true;
|
redirectCalled = true;
|
||||||
dataSourceUnderTest.onRedirectReceived(
|
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfoWithUrl("http://example.com/video", 300),
|
createUrlResponseInfoWithUrl("http://example.com/video", 300),
|
||||||
"http://example.com/video/redirect");
|
"http://example.com/video/redirect");
|
||||||
} else {
|
} else {
|
||||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
||||||
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -939,7 +938,8 @@ public final class CronetDataSourceTest {
|
||||||
new Answer<Object>() {
|
new Answer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
||||||
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -952,7 +952,7 @@ public final class CronetDataSourceTest {
|
||||||
new Answer<Object>() {
|
new Answer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
dataSourceUnderTest.onFailed(
|
dataSourceUnderTest.urlRequestCallback.onFailed(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfo(500), // statusCode
|
createUrlResponseInfo(500), // statusCode
|
||||||
mockNetworkException);
|
mockNetworkException);
|
||||||
|
|
@ -970,14 +970,15 @@ public final class CronetDataSourceTest {
|
||||||
@Override
|
@Override
|
||||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||||
if (positionAndRemaining[1] == 0) {
|
if (positionAndRemaining[1] == 0) {
|
||||||
dataSourceUnderTest.onSucceeded(mockUrlRequest, testUrlResponseInfo);
|
dataSourceUnderTest.urlRequestCallback.onSucceeded(
|
||||||
|
mockUrlRequest, testUrlResponseInfo);
|
||||||
} else {
|
} else {
|
||||||
ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0];
|
ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0];
|
||||||
int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining());
|
int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining());
|
||||||
inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength));
|
inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength));
|
||||||
positionAndRemaining[0] += readLength;
|
positionAndRemaining[0] += readLength;
|
||||||
positionAndRemaining[1] -= readLength;
|
positionAndRemaining[1] -= readLength;
|
||||||
dataSourceUnderTest.onReadCompleted(
|
dataSourceUnderTest.urlRequestCallback.onReadCompleted(
|
||||||
mockUrlRequest, testUrlResponseInfo, inputBuffer);
|
mockUrlRequest, testUrlResponseInfo, inputBuffer);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -992,7 +993,7 @@ public final class CronetDataSourceTest {
|
||||||
new Answer<Object>() {
|
new Answer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
dataSourceUnderTest.onFailed(
|
dataSourceUnderTest.urlRequestCallback.onFailed(
|
||||||
mockUrlRequest,
|
mockUrlRequest,
|
||||||
createUrlResponseInfo(500), // statusCode
|
createUrlResponseInfo(500), // statusCode
|
||||||
mockNetworkException);
|
mockNetworkException);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
||||||
|
import com.google.android.exoplayer2.upstream.BaseDataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSourceException;
|
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
|
|
@ -42,10 +44,8 @@ import okhttp3.Request;
|
||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
/**
|
/** An {@link HttpDataSource} that delegates to Square's {@link Call.Factory}. */
|
||||||
* An {@link HttpDataSource} that delegates to Square's {@link Call.Factory}.
|
public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
*/
|
|
||||||
public class OkHttpDataSource implements HttpDataSource {
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ExoPlayerLibraryInfo.registerModule("goog.exo.okhttp");
|
ExoPlayerLibraryInfo.registerModule("goog.exo.okhttp");
|
||||||
|
|
@ -58,7 +58,6 @@ public class OkHttpDataSource implements HttpDataSource {
|
||||||
|
|
||||||
@Nullable private final String userAgent;
|
@Nullable private final String userAgent;
|
||||||
@Nullable private final Predicate<String> contentTypePredicate;
|
@Nullable private final Predicate<String> contentTypePredicate;
|
||||||
@Nullable private final TransferListener<? super OkHttpDataSource> listener;
|
|
||||||
@Nullable private final CacheControl cacheControl;
|
@Nullable private final CacheControl cacheControl;
|
||||||
@Nullable private final RequestProperties defaultRequestProperties;
|
@Nullable private final RequestProperties defaultRequestProperties;
|
||||||
|
|
||||||
|
|
@ -90,13 +89,15 @@ public class OkHttpDataSource implements HttpDataSource {
|
||||||
* by the source.
|
* by the source.
|
||||||
* @param userAgent An optional User-Agent string.
|
* @param userAgent An optional User-Agent string.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a {@link InvalidContentTypeException} is thrown from
|
* predicate then a {@link InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
*/
|
*/
|
||||||
public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent,
|
public OkHttpDataSource(
|
||||||
|
@NonNull Call.Factory callFactory,
|
||||||
|
@Nullable String userAgent,
|
||||||
@Nullable Predicate<String> contentTypePredicate,
|
@Nullable Predicate<String> contentTypePredicate,
|
||||||
@Nullable TransferListener<? super OkHttpDataSource> listener) {
|
@Nullable TransferListener<? super DataSource> listener) {
|
||||||
this(callFactory, userAgent, contentTypePredicate, listener, null, null);
|
this(callFactory, userAgent, contentTypePredicate, listener, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,24 +106,30 @@ public class OkHttpDataSource implements HttpDataSource {
|
||||||
* by the source.
|
* by the source.
|
||||||
* @param userAgent An optional User-Agent string.
|
* @param userAgent An optional User-Agent string.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a {@link InvalidContentTypeException} is thrown from
|
* predicate then a {@link InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
* @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header.
|
* @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header.
|
||||||
* @param defaultRequestProperties The optional default {@link RequestProperties} to be sent to
|
* @param defaultRequestProperties The optional default {@link RequestProperties} to be sent to
|
||||||
* the server as HTTP headers on every request.
|
* the server as HTTP headers on every request.
|
||||||
*/
|
*/
|
||||||
public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent,
|
public OkHttpDataSource(
|
||||||
|
@NonNull Call.Factory callFactory,
|
||||||
|
@Nullable String userAgent,
|
||||||
@Nullable Predicate<String> contentTypePredicate,
|
@Nullable Predicate<String> contentTypePredicate,
|
||||||
@Nullable TransferListener<? super OkHttpDataSource> listener,
|
@Nullable TransferListener<? super DataSource> listener,
|
||||||
@Nullable CacheControl cacheControl, @Nullable RequestProperties defaultRequestProperties) {
|
@Nullable CacheControl cacheControl,
|
||||||
|
@Nullable RequestProperties defaultRequestProperties) {
|
||||||
|
super(DataSource.TYPE_REMOTE);
|
||||||
this.callFactory = Assertions.checkNotNull(callFactory);
|
this.callFactory = Assertions.checkNotNull(callFactory);
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
this.listener = listener;
|
|
||||||
this.cacheControl = cacheControl;
|
this.cacheControl = cacheControl;
|
||||||
this.defaultRequestProperties = defaultRequestProperties;
|
this.defaultRequestProperties = defaultRequestProperties;
|
||||||
this.requestProperties = new RequestProperties();
|
this.requestProperties = new RequestProperties();
|
||||||
|
if (listener != null) {
|
||||||
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -203,9 +210,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesToRead;
|
return bytesToRead;
|
||||||
}
|
}
|
||||||
|
|
@ -224,9 +229,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
||||||
public void close() throws HttpDataSourceException {
|
public void close() throws HttpDataSourceException {
|
||||||
if (opened) {
|
if (opened) {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
closeConnectionQuietly();
|
closeConnectionQuietly();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -333,9 +336,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
bytesSkipped += read;
|
bytesSkipped += read;
|
||||||
if (listener != null) {
|
bytesTransferred(read);
|
||||||
listener.onBytesTransferred(this, read);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release the shared skip buffer.
|
// Release the shared skip buffer.
|
||||||
|
|
@ -378,9 +379,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesRead += read;
|
bytesRead += read;
|
||||||
if (listener != null) {
|
bytesTransferred(read);
|
||||||
listener.onBytesTransferred(this, read);
|
|
||||||
}
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import android.net.Uri;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
||||||
|
import com.google.android.exoplayer2.upstream.BaseDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
|
|
@ -26,17 +27,13 @@ import java.io.IOException;
|
||||||
import net.butterflytv.rtmp_client.RtmpClient;
|
import net.butterflytv.rtmp_client.RtmpClient;
|
||||||
import net.butterflytv.rtmp_client.RtmpClient.RtmpIOException;
|
import net.butterflytv.rtmp_client.RtmpClient.RtmpIOException;
|
||||||
|
|
||||||
/**
|
/** A Real-Time Messaging Protocol (RTMP) {@link DataSource}. */
|
||||||
* A Real-Time Messaging Protocol (RTMP) {@link DataSource}.
|
public final class RtmpDataSource extends BaseDataSource {
|
||||||
*/
|
|
||||||
public final class RtmpDataSource implements DataSource {
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ExoPlayerLibraryInfo.registerModule("goog.exo.rtmp");
|
ExoPlayerLibraryInfo.registerModule("goog.exo.rtmp");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable private final TransferListener<? super RtmpDataSource> listener;
|
|
||||||
|
|
||||||
private RtmpClient rtmpClient;
|
private RtmpClient rtmpClient;
|
||||||
private Uri uri;
|
private Uri uri;
|
||||||
|
|
||||||
|
|
@ -44,11 +41,12 @@ public final class RtmpDataSource implements DataSource {
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param listener An optional listener. */
|
||||||
* @param listener An optional listener.
|
public RtmpDataSource(@Nullable TransferListener<? super DataSource> listener) {
|
||||||
*/
|
super(DataSource.TYPE_REMOTE);
|
||||||
public RtmpDataSource(@Nullable TransferListener<? super RtmpDataSource> listener) {
|
if (listener != null) {
|
||||||
this.listener = listener;
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -57,9 +55,7 @@ public final class RtmpDataSource implements DataSource {
|
||||||
rtmpClient.open(dataSpec.uri.toString(), false);
|
rtmpClient.open(dataSpec.uri.toString(), false);
|
||||||
|
|
||||||
this.uri = dataSpec.uri;
|
this.uri = dataSpec.uri;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
return C.LENGTH_UNSET;
|
return C.LENGTH_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,9 +65,7 @@ public final class RtmpDataSource implements DataSource {
|
||||||
if (bytesRead == -1) {
|
if (bytesRead == -1) {
|
||||||
return C.RESULT_END_OF_INPUT;
|
return C.RESULT_END_OF_INPUT;
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
bytesTransferred(bytesRead);
|
||||||
listener.onBytesTransferred(this, bytesRead);
|
|
||||||
}
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,9 +73,7 @@ public final class RtmpDataSource implements DataSource {
|
||||||
public void close() {
|
public void close() {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
uri = null;
|
uri = null;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (rtmpClient != null) {
|
if (rtmpClient != null) {
|
||||||
rtmpClient.close();
|
rtmpClient.close();
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,14 @@ import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
*/
|
*/
|
||||||
public final class RtmpDataSourceFactory implements DataSource.Factory {
|
public final class RtmpDataSourceFactory implements DataSource.Factory {
|
||||||
|
|
||||||
@Nullable
|
private final @Nullable TransferListener<? super DataSource> listener;
|
||||||
private final TransferListener<? super RtmpDataSource> listener;
|
|
||||||
|
|
||||||
public RtmpDataSourceFactory() {
|
public RtmpDataSourceFactory() {
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param listener An optional listener. */
|
||||||
* @param listener An optional listener.
|
public RtmpDataSourceFactory(@Nullable TransferListener<? super DataSource> listener) {
|
||||||
*/
|
|
||||||
public RtmpDataSourceFactory(@Nullable TransferListener<? super RtmpDataSource> listener) {
|
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,14 @@ package com.google.android.exoplayer2.upstream;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/** A {@link DataSource} for reading from a local asset. */
|
||||||
* A {@link DataSource} for reading from a local asset.
|
public final class AssetDataSource extends BaseDataSource {
|
||||||
*/
|
|
||||||
public final class AssetDataSource implements DataSource {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when an {@link IOException} is encountered reading a local asset.
|
* Thrown when an {@link IOException} is encountered reading a local asset.
|
||||||
|
|
@ -40,10 +39,9 @@ public final class AssetDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AssetManager assetManager;
|
private final AssetManager assetManager;
|
||||||
private final TransferListener<? super AssetDataSource> listener;
|
|
||||||
|
|
||||||
private Uri uri;
|
private @Nullable Uri uri;
|
||||||
private InputStream inputStream;
|
private @Nullable InputStream inputStream;
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
private boolean opened;
|
private boolean opened;
|
||||||
|
|
||||||
|
|
@ -58,9 +56,12 @@ public final class AssetDataSource implements DataSource {
|
||||||
* @param context A context.
|
* @param context A context.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
*/
|
*/
|
||||||
public AssetDataSource(Context context, TransferListener<? super AssetDataSource> listener) {
|
public AssetDataSource(Context context, @Nullable TransferListener<? super DataSource> listener) {
|
||||||
|
super(DataSource.TYPE_LOCAL);
|
||||||
this.assetManager = context.getAssets();
|
this.assetManager = context.getAssets();
|
||||||
this.listener = listener;
|
if (listener != null) {
|
||||||
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -96,9 +97,7 @@ public final class AssetDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
return bytesRemaining;
|
return bytesRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,14 +128,12 @@ public final class AssetDataSource implements DataSource {
|
||||||
if (bytesRemaining != C.LENGTH_UNSET) {
|
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||||
bytesRemaining -= bytesRead;
|
bytesRemaining -= bytesRead;
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
bytesTransferred(bytesRead);
|
||||||
listener.onBytesTransferred(this, bytesRead);
|
|
||||||
}
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getUri() {
|
public @Nullable Uri getUri() {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,9 +150,7 @@ public final class AssetDataSource implements DataSource {
|
||||||
inputStream = null;
|
inputStream = null;
|
||||||
if (opened) {
|
if (opened) {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,25 +16,26 @@
|
||||||
package com.google.android.exoplayer2.upstream;
|
package com.google.android.exoplayer2.upstream;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/** A {@link DataSource} for reading from a byte array. */
|
||||||
* A {@link DataSource} for reading from a byte array.
|
public final class ByteArrayDataSource extends BaseDataSource {
|
||||||
*/
|
|
||||||
public final class ByteArrayDataSource implements DataSource {
|
|
||||||
|
|
||||||
private final byte[] data;
|
private final byte[] data;
|
||||||
|
|
||||||
private Uri uri;
|
private @Nullable Uri uri;
|
||||||
private int readPosition;
|
private int readPosition;
|
||||||
private int bytesRemaining;
|
private int bytesRemaining;
|
||||||
|
private boolean opened;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param data The data to be read.
|
* @param data The data to be read.
|
||||||
*/
|
*/
|
||||||
public ByteArrayDataSource(byte[] data) {
|
public ByteArrayDataSource(byte[] data) {
|
||||||
|
super(DataSource.TYPE_LOCAL);
|
||||||
Assertions.checkNotNull(data);
|
Assertions.checkNotNull(data);
|
||||||
Assertions.checkArgument(data.length > 0);
|
Assertions.checkArgument(data.length > 0);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
@ -50,6 +51,8 @@ public final class ByteArrayDataSource implements DataSource {
|
||||||
throw new IOException("Unsatisfiable range: [" + readPosition + ", " + dataSpec.length
|
throw new IOException("Unsatisfiable range: [" + readPosition + ", " + dataSpec.length
|
||||||
+ "], length: " + data.length);
|
+ "], length: " + data.length);
|
||||||
}
|
}
|
||||||
|
opened = true;
|
||||||
|
transferStarted(dataSpec);
|
||||||
return bytesRemaining;
|
return bytesRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,16 +68,21 @@ public final class ByteArrayDataSource implements DataSource {
|
||||||
System.arraycopy(data, readPosition, buffer, offset, readLength);
|
System.arraycopy(data, readPosition, buffer, offset, readLength);
|
||||||
readPosition += readLength;
|
readPosition += readLength;
|
||||||
bytesRemaining -= readLength;
|
bytesRemaining -= readLength;
|
||||||
|
bytesTransferred(readLength);
|
||||||
return readLength;
|
return readLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getUri() {
|
public @Nullable Uri getUri() {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
if (opened) {
|
||||||
|
opened = false;
|
||||||
|
transferEnded();
|
||||||
|
}
|
||||||
uri = null;
|
uri = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.AssetFileDescriptor;
|
import android.content.res.AssetFileDescriptor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
|
@ -26,10 +27,8 @@ import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
|
||||||
/**
|
/** A {@link DataSource} for reading from a content URI. */
|
||||||
* A {@link DataSource} for reading from a content URI.
|
public final class ContentDataSource extends BaseDataSource {
|
||||||
*/
|
|
||||||
public final class ContentDataSource implements DataSource {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when an {@link IOException} is encountered reading from a content URI.
|
* Thrown when an {@link IOException} is encountered reading from a content URI.
|
||||||
|
|
@ -43,11 +42,10 @@ public final class ContentDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ContentResolver resolver;
|
private final ContentResolver resolver;
|
||||||
private final TransferListener<? super ContentDataSource> listener;
|
|
||||||
|
|
||||||
private Uri uri;
|
private @Nullable Uri uri;
|
||||||
private AssetFileDescriptor assetFileDescriptor;
|
private @Nullable AssetFileDescriptor assetFileDescriptor;
|
||||||
private FileInputStream inputStream;
|
private @Nullable FileInputStream inputStream;
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
private boolean opened;
|
private boolean opened;
|
||||||
|
|
||||||
|
|
@ -62,9 +60,13 @@ public final class ContentDataSource implements DataSource {
|
||||||
* @param context A context.
|
* @param context A context.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
*/
|
*/
|
||||||
public ContentDataSource(Context context, TransferListener<? super ContentDataSource> listener) {
|
public ContentDataSource(
|
||||||
|
Context context, @Nullable TransferListener<? super DataSource> listener) {
|
||||||
|
super(DataSource.TYPE_LOCAL);
|
||||||
this.resolver = context.getContentResolver();
|
this.resolver = context.getContentResolver();
|
||||||
this.listener = listener;
|
if (listener != null) {
|
||||||
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -102,9 +104,7 @@ public final class ContentDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesRemaining;
|
return bytesRemaining;
|
||||||
}
|
}
|
||||||
|
|
@ -136,14 +136,12 @@ public final class ContentDataSource implements DataSource {
|
||||||
if (bytesRemaining != C.LENGTH_UNSET) {
|
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||||
bytesRemaining -= bytesRead;
|
bytesRemaining -= bytesRead;
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
bytesTransferred(bytesRead);
|
||||||
listener.onBytesTransferred(this, bytesRead);
|
|
||||||
}
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getUri() {
|
public @Nullable Uri getUri() {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,9 +166,7 @@ public final class ContentDataSource implements DataSource {
|
||||||
assetFileDescriptor = null;
|
assetFileDescriptor = null;
|
||||||
if (opened) {
|
if (opened) {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
package com.google.android.exoplayer2.upstream;
|
package com.google.android.exoplayer2.upstream;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ParserException;
|
import com.google.android.exoplayer2.ParserException;
|
||||||
|
|
@ -23,16 +24,18 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
|
||||||
/**
|
/** A {@link DataSource} for reading data URLs, as defined by RFC 2397. */
|
||||||
* A {@link DataSource} for reading data URLs, as defined by RFC 2397.
|
public final class DataSchemeDataSource extends BaseDataSource {
|
||||||
*/
|
|
||||||
public final class DataSchemeDataSource implements DataSource {
|
|
||||||
|
|
||||||
public static final String SCHEME_DATA = "data";
|
public static final String SCHEME_DATA = "data";
|
||||||
|
|
||||||
private DataSpec dataSpec;
|
private @Nullable DataSpec dataSpec;
|
||||||
private int bytesRead;
|
private int bytesRead;
|
||||||
private byte[] data;
|
private @Nullable byte[] data;
|
||||||
|
|
||||||
|
public DataSchemeDataSource() {
|
||||||
|
super(DataSource.TYPE_LOCAL);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws IOException {
|
public long open(DataSpec dataSpec) throws IOException {
|
||||||
|
|
@ -57,6 +60,7 @@ public final class DataSchemeDataSource implements DataSource {
|
||||||
// TODO: Add support for other charsets.
|
// TODO: Add support for other charsets.
|
||||||
data = URLDecoder.decode(dataString, C.ASCII_NAME).getBytes();
|
data = URLDecoder.decode(dataString, C.ASCII_NAME).getBytes();
|
||||||
}
|
}
|
||||||
|
transferStarted(dataSpec);
|
||||||
return data.length;
|
return data.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,18 +76,22 @@ public final class DataSchemeDataSource implements DataSource {
|
||||||
readLength = Math.min(readLength, remainingBytes);
|
readLength = Math.min(readLength, remainingBytes);
|
||||||
System.arraycopy(data, bytesRead, buffer, offset, readLength);
|
System.arraycopy(data, bytesRead, buffer, offset, readLength);
|
||||||
bytesRead += readLength;
|
bytesRead += readLength;
|
||||||
|
bytesTransferred(readLength);
|
||||||
return readLength;
|
return readLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getUri() {
|
public @Nullable Uri getUri() {
|
||||||
return dataSpec != null ? dataSpec.uri : null;
|
return dataSpec != null ? dataSpec.uri : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
if (data != null) {
|
||||||
|
data = null;
|
||||||
|
transferEnded();
|
||||||
|
}
|
||||||
dataSpec = null;
|
dataSpec = null;
|
||||||
data = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
package com.google.android.exoplayer2.upstream;
|
package com.google.android.exoplayer2.upstream;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
|
@ -41,13 +42,13 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}.
|
* An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}.
|
||||||
* <p>
|
*
|
||||||
* By default this implementation will not follow cross-protocol redirects (i.e. redirects from
|
* <p>By default this implementation will not follow cross-protocol redirects (i.e. redirects from
|
||||||
* HTTP to HTTPS or vice versa). Cross-protocol redirects can be enabled by using the
|
* HTTP to HTTPS or vice versa). Cross-protocol redirects can be enabled by using the {@link
|
||||||
* {@link #DefaultHttpDataSource(String, Predicate, TransferListener, int, int, boolean,
|
* #DefaultHttpDataSource(String, Predicate, TransferListener, int, int, boolean,
|
||||||
* RequestProperties)} constructor and passing {@code true} as the second last argument.
|
* RequestProperties)} constructor and passing {@code true} as the second last argument.
|
||||||
*/
|
*/
|
||||||
public class DefaultHttpDataSource implements HttpDataSource {
|
public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default connection timeout, in milliseconds.
|
* The default connection timeout, in milliseconds.
|
||||||
|
|
@ -69,14 +70,13 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
private final int connectTimeoutMillis;
|
private final int connectTimeoutMillis;
|
||||||
private final int readTimeoutMillis;
|
private final int readTimeoutMillis;
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
private final Predicate<String> contentTypePredicate;
|
private final @Nullable Predicate<String> contentTypePredicate;
|
||||||
private final RequestProperties defaultRequestProperties;
|
private final @Nullable RequestProperties defaultRequestProperties;
|
||||||
private final RequestProperties requestProperties;
|
private final RequestProperties requestProperties;
|
||||||
private final TransferListener<? super DefaultHttpDataSource> listener;
|
|
||||||
|
|
||||||
private DataSpec dataSpec;
|
private @Nullable DataSpec dataSpec;
|
||||||
private HttpURLConnection connection;
|
private @Nullable HttpURLConnection connection;
|
||||||
private InputStream inputStream;
|
private @Nullable InputStream inputStream;
|
||||||
private boolean opened;
|
private boolean opened;
|
||||||
|
|
||||||
private long bytesToSkip;
|
private long bytesToSkip;
|
||||||
|
|
@ -88,22 +88,24 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
/**
|
/**
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent The User-Agent string that should be used.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from
|
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
*/
|
*/
|
||||||
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate) {
|
public DefaultHttpDataSource(String userAgent, @Nullable Predicate<String> contentTypePredicate) {
|
||||||
this(userAgent, contentTypePredicate, null);
|
this(userAgent, contentTypePredicate, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent The User-Agent string that should be used.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from
|
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
*/
|
*/
|
||||||
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
|
public DefaultHttpDataSource(
|
||||||
TransferListener<? super DefaultHttpDataSource> listener) {
|
String userAgent,
|
||||||
|
@Nullable Predicate<String> contentTypePredicate,
|
||||||
|
@Nullable TransferListener<? super DataSource> listener) {
|
||||||
this(userAgent, contentTypePredicate, listener, DEFAULT_CONNECT_TIMEOUT_MILLIS,
|
this(userAgent, contentTypePredicate, listener, DEFAULT_CONNECT_TIMEOUT_MILLIS,
|
||||||
DEFAULT_READ_TIMEOUT_MILLIS);
|
DEFAULT_READ_TIMEOUT_MILLIS);
|
||||||
}
|
}
|
||||||
|
|
@ -111,16 +113,19 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
/**
|
/**
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent The User-Agent string that should be used.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from
|
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
* @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is
|
* @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is
|
||||||
* interpreted as an infinite timeout.
|
* interpreted as an infinite timeout.
|
||||||
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted
|
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as
|
||||||
* as an infinite timeout.
|
* an infinite timeout.
|
||||||
*/
|
*/
|
||||||
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
|
public DefaultHttpDataSource(
|
||||||
TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis,
|
String userAgent,
|
||||||
|
@Nullable Predicate<String> contentTypePredicate,
|
||||||
|
@Nullable TransferListener<? super DataSource> listener,
|
||||||
|
int connectTimeoutMillis,
|
||||||
int readTimeoutMillis) {
|
int readTimeoutMillis) {
|
||||||
this(userAgent, contentTypePredicate, listener, connectTimeoutMillis, readTimeoutMillis, false,
|
this(userAgent, contentTypePredicate, listener, connectTimeoutMillis, readTimeoutMillis, false,
|
||||||
null);
|
null);
|
||||||
|
|
@ -129,35 +134,42 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
/**
|
/**
|
||||||
* @param userAgent The User-Agent string that should be used.
|
* @param userAgent The User-Agent string that should be used.
|
||||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from
|
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
|
||||||
* {@link #open(DataSpec)}.
|
* #open(DataSpec)}.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
* @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is
|
* @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is
|
||||||
* interpreted as an infinite timeout. Pass {@link #DEFAULT_CONNECT_TIMEOUT_MILLIS} to use
|
* interpreted as an infinite timeout. Pass {@link #DEFAULT_CONNECT_TIMEOUT_MILLIS} to use the
|
||||||
* the default value.
|
* default value.
|
||||||
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted
|
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as
|
||||||
* as an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value.
|
* an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value.
|
||||||
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
|
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
|
||||||
* to HTTPS and vice versa) are enabled.
|
* to HTTPS and vice versa) are enabled.
|
||||||
* @param defaultRequestProperties The default request properties to be sent to the server as
|
* @param defaultRequestProperties The default request properties to be sent to the server as HTTP
|
||||||
* HTTP headers or {@code null} if not required.
|
* headers or {@code null} if not required.
|
||||||
*/
|
*/
|
||||||
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
|
public DefaultHttpDataSource(
|
||||||
TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis,
|
String userAgent,
|
||||||
int readTimeoutMillis, boolean allowCrossProtocolRedirects,
|
@Nullable Predicate<String> contentTypePredicate,
|
||||||
RequestProperties defaultRequestProperties) {
|
@Nullable TransferListener<? super DataSource> listener,
|
||||||
|
int connectTimeoutMillis,
|
||||||
|
int readTimeoutMillis,
|
||||||
|
boolean allowCrossProtocolRedirects,
|
||||||
|
@Nullable RequestProperties defaultRequestProperties) {
|
||||||
|
super(DataSource.TYPE_REMOTE);
|
||||||
this.userAgent = Assertions.checkNotEmpty(userAgent);
|
this.userAgent = Assertions.checkNotEmpty(userAgent);
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
this.listener = listener;
|
|
||||||
this.requestProperties = new RequestProperties();
|
this.requestProperties = new RequestProperties();
|
||||||
this.connectTimeoutMillis = connectTimeoutMillis;
|
this.connectTimeoutMillis = connectTimeoutMillis;
|
||||||
this.readTimeoutMillis = readTimeoutMillis;
|
this.readTimeoutMillis = readTimeoutMillis;
|
||||||
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
|
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
|
||||||
this.defaultRequestProperties = defaultRequestProperties;
|
this.defaultRequestProperties = defaultRequestProperties;
|
||||||
|
if (listener != null) {
|
||||||
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getUri() {
|
public @Nullable Uri getUri() {
|
||||||
return connection == null ? null : Uri.parse(connection.getURL().toString());
|
return connection == null ? null : Uri.parse(connection.getURL().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,9 +266,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesToRead;
|
return bytesToRead;
|
||||||
}
|
}
|
||||||
|
|
@ -287,9 +297,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
closeConnectionQuietly();
|
closeConnectionQuietly();
|
||||||
if (opened) {
|
if (opened) {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -534,9 +542,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
bytesSkipped += read;
|
bytesSkipped += read;
|
||||||
if (listener != null) {
|
bytesTransferred(read);
|
||||||
listener.onBytesTransferred(this, read);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release the shared skip buffer.
|
// Release the shared skip buffer.
|
||||||
|
|
@ -579,9 +585,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesRead += read;
|
bytesRead += read;
|
||||||
if (listener != null) {
|
bytesTransferred(read);
|
||||||
listener.onBytesTransferred(this, read);
|
|
||||||
}
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,14 @@
|
||||||
package com.google.android.exoplayer2.upstream;
|
package com.google.android.exoplayer2.upstream;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
|
|
||||||
/**
|
/** A {@link DataSource} for reading local files. */
|
||||||
* A {@link DataSource} for reading local files.
|
public final class FileDataSource extends BaseDataSource {
|
||||||
*/
|
|
||||||
public final class FileDataSource implements DataSource {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when IOException is encountered during local file read operation.
|
* Thrown when IOException is encountered during local file read operation.
|
||||||
|
|
@ -37,10 +36,8 @@ public final class FileDataSource implements DataSource {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final TransferListener<? super FileDataSource> listener;
|
private @Nullable RandomAccessFile file;
|
||||||
|
private @Nullable Uri uri;
|
||||||
private RandomAccessFile file;
|
|
||||||
private Uri uri;
|
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
private boolean opened;
|
private boolean opened;
|
||||||
|
|
||||||
|
|
@ -48,11 +45,12 @@ public final class FileDataSource implements DataSource {
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @param listener An optional listener. */
|
||||||
* @param listener An optional listener.
|
public FileDataSource(@Nullable TransferListener<? super DataSource> listener) {
|
||||||
*/
|
super(DataSource.TYPE_LOCAL);
|
||||||
public FileDataSource(TransferListener<? super FileDataSource> listener) {
|
if (listener != null) {
|
||||||
this.listener = listener;
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -71,9 +69,7 @@ public final class FileDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesRemaining;
|
return bytesRemaining;
|
||||||
}
|
}
|
||||||
|
|
@ -94,9 +90,7 @@ public final class FileDataSource implements DataSource {
|
||||||
|
|
||||||
if (bytesRead > 0) {
|
if (bytesRead > 0) {
|
||||||
bytesRemaining -= bytesRead;
|
bytesRemaining -= bytesRead;
|
||||||
if (listener != null) {
|
bytesTransferred(bytesRead);
|
||||||
listener.onBytesTransferred(this, bytesRead);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
|
|
@ -104,7 +98,7 @@ public final class FileDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getUri() {
|
public @Nullable Uri getUri() {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,9 +115,7 @@ public final class FileDataSource implements DataSource {
|
||||||
file = null;
|
file = null;
|
||||||
if (opened) {
|
if (opened) {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,20 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.upstream;
|
package com.google.android.exoplayer2.upstream;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link DataSource.Factory} that produces {@link FileDataSource}.
|
* A {@link DataSource.Factory} that produces {@link FileDataSource}.
|
||||||
*/
|
*/
|
||||||
public final class FileDataSourceFactory implements DataSource.Factory {
|
public final class FileDataSourceFactory implements DataSource.Factory {
|
||||||
|
|
||||||
private final TransferListener<? super FileDataSource> listener;
|
private final @Nullable TransferListener<? super DataSource> listener;
|
||||||
|
|
||||||
public FileDataSourceFactory() {
|
public FileDataSourceFactory() {
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileDataSourceFactory(TransferListener<? super FileDataSource> listener) {
|
public FileDataSourceFactory(@Nullable TransferListener<? super DataSource> listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import android.content.Context;
|
||||||
import android.content.res.AssetFileDescriptor;
|
import android.content.res.AssetFileDescriptor;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
|
|
@ -28,12 +29,12 @@ import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link DataSource} for reading a raw resource inside the APK.
|
* A {@link DataSource} for reading a raw resource inside the APK.
|
||||||
* <p>
|
*
|
||||||
* URIs supported by this source are of the form {@code rawresource:///rawResourceId}, where
|
* <p>URIs supported by this source are of the form {@code rawresource:///rawResourceId}, where
|
||||||
* rawResourceId is the integer identifier of a raw resource. {@link #buildRawResourceUri(int)} can
|
* rawResourceId is the integer identifier of a raw resource. {@link #buildRawResourceUri(int)} can
|
||||||
* be used to build {@link Uri}s in this format.
|
* be used to build {@link Uri}s in this format.
|
||||||
*/
|
*/
|
||||||
public final class RawResourceDataSource implements DataSource {
|
public final class RawResourceDataSource extends BaseDataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when an {@link IOException} is encountered reading from a raw resource.
|
* Thrown when an {@link IOException} is encountered reading from a raw resource.
|
||||||
|
|
@ -62,11 +63,10 @@ public final class RawResourceDataSource implements DataSource {
|
||||||
public static final String RAW_RESOURCE_SCHEME = "rawresource";
|
public static final String RAW_RESOURCE_SCHEME = "rawresource";
|
||||||
|
|
||||||
private final Resources resources;
|
private final Resources resources;
|
||||||
private final TransferListener<? super RawResourceDataSource> listener;
|
|
||||||
|
|
||||||
private Uri uri;
|
private @Nullable Uri uri;
|
||||||
private AssetFileDescriptor assetFileDescriptor;
|
private @Nullable AssetFileDescriptor assetFileDescriptor;
|
||||||
private InputStream inputStream;
|
private @Nullable InputStream inputStream;
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
private boolean opened;
|
private boolean opened;
|
||||||
|
|
||||||
|
|
@ -81,10 +81,13 @@ public final class RawResourceDataSource implements DataSource {
|
||||||
* @param context A context.
|
* @param context A context.
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
*/
|
*/
|
||||||
public RawResourceDataSource(Context context,
|
public RawResourceDataSource(
|
||||||
TransferListener<? super RawResourceDataSource> listener) {
|
Context context, @Nullable TransferListener<? super DataSource> listener) {
|
||||||
|
super(DataSource.TYPE_LOCAL);
|
||||||
this.resources = context.getResources();
|
this.resources = context.getResources();
|
||||||
this.listener = listener;
|
if (listener != null) {
|
||||||
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -124,9 +127,7 @@ public final class RawResourceDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesRemaining;
|
return bytesRemaining;
|
||||||
}
|
}
|
||||||
|
|
@ -158,14 +159,12 @@ public final class RawResourceDataSource implements DataSource {
|
||||||
if (bytesRemaining != C.LENGTH_UNSET) {
|
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||||
bytesRemaining -= bytesRead;
|
bytesRemaining -= bytesRead;
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
bytesTransferred(bytesRead);
|
||||||
listener.onBytesTransferred(this, bytesRead);
|
|
||||||
}
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getUri() {
|
public @Nullable Uri getUri() {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,9 +189,7 @@ public final class RawResourceDataSource implements DataSource {
|
||||||
assetFileDescriptor = null;
|
assetFileDescriptor = null;
|
||||||
if (opened) {
|
if (opened) {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
package com.google.android.exoplayer2.upstream;
|
package com.google.android.exoplayer2.upstream;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
|
|
@ -25,10 +26,8 @@ import java.net.InetSocketAddress;
|
||||||
import java.net.MulticastSocket;
|
import java.net.MulticastSocket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
|
||||||
/**
|
/** A UDP {@link DataSource}. */
|
||||||
* A UDP {@link DataSource}.
|
public final class UdpDataSource extends BaseDataSource {
|
||||||
*/
|
|
||||||
public final class UdpDataSource implements DataSource {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when an error is encountered when trying to read from a {@link UdpDataSource}.
|
* Thrown when an error is encountered when trying to read from a {@link UdpDataSource}.
|
||||||
|
|
@ -51,24 +50,21 @@ public final class UdpDataSource implements DataSource {
|
||||||
*/
|
*/
|
||||||
public static final int DEAFULT_SOCKET_TIMEOUT_MILLIS = 8 * 1000;
|
public static final int DEAFULT_SOCKET_TIMEOUT_MILLIS = 8 * 1000;
|
||||||
|
|
||||||
private final TransferListener<? super UdpDataSource> listener;
|
|
||||||
private final int socketTimeoutMillis;
|
private final int socketTimeoutMillis;
|
||||||
private final byte[] packetBuffer;
|
private final byte[] packetBuffer;
|
||||||
private final DatagramPacket packet;
|
private final DatagramPacket packet;
|
||||||
|
|
||||||
private Uri uri;
|
private @Nullable Uri uri;
|
||||||
private DatagramSocket socket;
|
private @Nullable DatagramSocket socket;
|
||||||
private MulticastSocket multicastSocket;
|
private @Nullable MulticastSocket multicastSocket;
|
||||||
private InetAddress address;
|
private @Nullable InetAddress address;
|
||||||
private InetSocketAddress socketAddress;
|
private @Nullable InetSocketAddress socketAddress;
|
||||||
private boolean opened;
|
private boolean opened;
|
||||||
|
|
||||||
private int packetRemaining;
|
private int packetRemaining;
|
||||||
|
|
||||||
/**
|
/** @param listener An optional listener. */
|
||||||
* @param listener An optional listener.
|
public UdpDataSource(@Nullable TransferListener<? super DataSource> listener) {
|
||||||
*/
|
|
||||||
public UdpDataSource(TransferListener<? super UdpDataSource> listener) {
|
|
||||||
this(listener, DEFAULT_MAX_PACKET_SIZE);
|
this(listener, DEFAULT_MAX_PACKET_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +72,7 @@ public final class UdpDataSource implements DataSource {
|
||||||
* @param listener An optional listener.
|
* @param listener An optional listener.
|
||||||
* @param maxPacketSize The maximum datagram packet size, in bytes.
|
* @param maxPacketSize The maximum datagram packet size, in bytes.
|
||||||
*/
|
*/
|
||||||
public UdpDataSource(TransferListener<? super UdpDataSource> listener, int maxPacketSize) {
|
public UdpDataSource(@Nullable TransferListener<? super DataSource> listener, int maxPacketSize) {
|
||||||
this(listener, maxPacketSize, DEAFULT_SOCKET_TIMEOUT_MILLIS);
|
this(listener, maxPacketSize, DEAFULT_SOCKET_TIMEOUT_MILLIS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,12 +82,17 @@ public final class UdpDataSource implements DataSource {
|
||||||
* @param socketTimeoutMillis The socket timeout in milliseconds. A timeout of zero is interpreted
|
* @param socketTimeoutMillis The socket timeout in milliseconds. A timeout of zero is interpreted
|
||||||
* as an infinite timeout.
|
* as an infinite timeout.
|
||||||
*/
|
*/
|
||||||
public UdpDataSource(TransferListener<? super UdpDataSource> listener, int maxPacketSize,
|
public UdpDataSource(
|
||||||
|
@Nullable TransferListener<? super DataSource> listener,
|
||||||
|
int maxPacketSize,
|
||||||
int socketTimeoutMillis) {
|
int socketTimeoutMillis) {
|
||||||
this.listener = listener;
|
super(DataSource.TYPE_REMOTE);
|
||||||
this.socketTimeoutMillis = socketTimeoutMillis;
|
this.socketTimeoutMillis = socketTimeoutMillis;
|
||||||
packetBuffer = new byte[maxPacketSize];
|
packetBuffer = new byte[maxPacketSize];
|
||||||
packet = new DatagramPacket(packetBuffer, 0, maxPacketSize);
|
packet = new DatagramPacket(packetBuffer, 0, maxPacketSize);
|
||||||
|
if (listener != null) {
|
||||||
|
addTransferListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -121,9 +122,7 @@ public final class UdpDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
if (listener != null) {
|
transferStarted(dataSpec);
|
||||||
listener.onTransferStart(this, dataSpec);
|
|
||||||
}
|
|
||||||
return C.LENGTH_UNSET;
|
return C.LENGTH_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,9 +140,7 @@ public final class UdpDataSource implements DataSource {
|
||||||
throw new UdpDataSourceException(e);
|
throw new UdpDataSourceException(e);
|
||||||
}
|
}
|
||||||
packetRemaining = packet.getLength();
|
packetRemaining = packet.getLength();
|
||||||
if (listener != null) {
|
bytesTransferred(packetRemaining);
|
||||||
listener.onBytesTransferred(this, packetRemaining);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int packetOffset = packet.getLength() - packetRemaining;
|
int packetOffset = packet.getLength() - packetRemaining;
|
||||||
|
|
@ -154,7 +151,7 @@ public final class UdpDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getUri() {
|
public @Nullable Uri getUri() {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,9 +175,7 @@ public final class UdpDataSource implements DataSource {
|
||||||
packetRemaining = 0;
|
packetRemaining = 0;
|
||||||
if (opened) {
|
if (opened) {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (listener != null) {
|
transferEnded();
|
||||||
listener.onTransferEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData;
|
import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment;
|
import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment;
|
||||||
|
import com.google.android.exoplayer2.upstream.BaseDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSourceException;
|
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
|
|
@ -32,18 +33,20 @@ import java.util.ArrayList;
|
||||||
* A fake {@link DataSource} capable of simulating various scenarios. It uses a {@link FakeDataSet}
|
* A fake {@link DataSource} capable of simulating various scenarios. It uses a {@link FakeDataSet}
|
||||||
* instance which determines the response to data access calls.
|
* instance which determines the response to data access calls.
|
||||||
*/
|
*/
|
||||||
public class FakeDataSource implements DataSource {
|
public class FakeDataSource extends BaseDataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory to create a {@link FakeDataSource}.
|
* Factory to create a {@link FakeDataSource}.
|
||||||
*/
|
*/
|
||||||
public static class Factory implements DataSource.Factory {
|
public static class Factory implements DataSource.Factory {
|
||||||
|
|
||||||
protected final TransferListener<? super FakeDataSource> transferListener;
|
protected final TransferListener<? super DataSource> transferListener;
|
||||||
protected FakeDataSet fakeDataSet;
|
protected FakeDataSet fakeDataSet;
|
||||||
|
protected @DataSource.Type int dataSourceType;
|
||||||
|
|
||||||
public Factory(@Nullable TransferListener<? super FakeDataSource> transferListener) {
|
public Factory(@Nullable TransferListener<? super DataSource> transferListener) {
|
||||||
this.transferListener = transferListener;
|
this.transferListener = transferListener;
|
||||||
|
this.dataSourceType = DataSource.TYPE_LOCAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Factory setFakeDataSet(FakeDataSet fakeDataSet) {
|
public final Factory setFakeDataSet(FakeDataSet fakeDataSet) {
|
||||||
|
|
@ -51,19 +54,23 @@ public class FakeDataSource implements DataSource {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public final Factory setDataSourceType(@DataSource.Type int dataSourceType) {
|
||||||
public DataSource createDataSource() {
|
this.dataSourceType = dataSourceType;
|
||||||
return new FakeDataSource(fakeDataSet, transferListener);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataSource createDataSource() {
|
||||||
|
return new FakeDataSource(fakeDataSet, transferListener, dataSourceType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final FakeDataSet fakeDataSet;
|
private final FakeDataSet fakeDataSet;
|
||||||
private final TransferListener<? super FakeDataSource> transferListener;
|
|
||||||
private final ArrayList<DataSpec> openedDataSpecs;
|
private final ArrayList<DataSpec> openedDataSpecs;
|
||||||
|
|
||||||
private Uri uri;
|
private Uri uri;
|
||||||
private boolean opened;
|
private boolean openCalled;
|
||||||
|
private boolean sourceOpened;
|
||||||
private FakeData fakeData;
|
private FakeData fakeData;
|
||||||
private int currentSegmentIndex;
|
private int currentSegmentIndex;
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
|
|
@ -73,15 +80,20 @@ public class FakeDataSource implements DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
public FakeDataSource(FakeDataSet fakeDataSet) {
|
public FakeDataSource(FakeDataSet fakeDataSet) {
|
||||||
this(fakeDataSet, null);
|
this(fakeDataSet, null, DataSource.TYPE_LOCAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FakeDataSource(FakeDataSet fakeDataSet,
|
public FakeDataSource(
|
||||||
@Nullable TransferListener<? super FakeDataSource> transferListener) {
|
FakeDataSet fakeDataSet,
|
||||||
|
@Nullable TransferListener<? super DataSource> transferListener,
|
||||||
|
@DataSource.Type int dataSourceType) {
|
||||||
|
super(dataSourceType);
|
||||||
Assertions.checkNotNull(fakeDataSet);
|
Assertions.checkNotNull(fakeDataSet);
|
||||||
this.fakeDataSet = fakeDataSet;
|
this.fakeDataSet = fakeDataSet;
|
||||||
this.transferListener = transferListener;
|
|
||||||
this.openedDataSpecs = new ArrayList<>();
|
this.openedDataSpecs = new ArrayList<>();
|
||||||
|
if (transferListener != null) {
|
||||||
|
addTransferListener(transferListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final FakeDataSet getDataSet() {
|
public final FakeDataSet getDataSet() {
|
||||||
|
|
@ -90,9 +102,9 @@ public class FakeDataSource implements DataSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final long open(DataSpec dataSpec) throws IOException {
|
public final long open(DataSpec dataSpec) throws IOException {
|
||||||
Assertions.checkState(!opened);
|
Assertions.checkState(!openCalled);
|
||||||
|
openCalled = true;
|
||||||
// DataSpec requires a matching close call even if open fails.
|
// DataSpec requires a matching close call even if open fails.
|
||||||
opened = true;
|
|
||||||
uri = dataSpec.uri;
|
uri = dataSpec.uri;
|
||||||
openedDataSpecs.add(dataSpec);
|
openedDataSpecs.add(dataSpec);
|
||||||
|
|
||||||
|
|
@ -129,9 +141,8 @@ public class FakeDataSource implements DataSource {
|
||||||
currentSegmentIndex++;
|
currentSegmentIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transferListener != null) {
|
sourceOpened = true;
|
||||||
transferListener.onTransferStart(this, dataSpec);
|
transferStarted(dataSpec);
|
||||||
}
|
|
||||||
// Configure bytesRemaining, and return.
|
// Configure bytesRemaining, and return.
|
||||||
if (dataSpec.length == C.LENGTH_UNSET) {
|
if (dataSpec.length == C.LENGTH_UNSET) {
|
||||||
bytesRemaining = totalLength - dataSpec.position;
|
bytesRemaining = totalLength - dataSpec.position;
|
||||||
|
|
@ -144,7 +155,7 @@ public class FakeDataSource implements DataSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int read(byte[] buffer, int offset, int readLength) throws IOException {
|
public final int read(byte[] buffer, int offset, int readLength) throws IOException {
|
||||||
Assertions.checkState(opened);
|
Assertions.checkState(sourceOpened);
|
||||||
while (true) {
|
while (true) {
|
||||||
if (currentSegmentIndex == fakeData.getSegments().size() || bytesRemaining == 0) {
|
if (currentSegmentIndex == fakeData.getSegments().size() || bytesRemaining == 0) {
|
||||||
return C.RESULT_END_OF_INPUT;
|
return C.RESULT_END_OF_INPUT;
|
||||||
|
|
@ -171,9 +182,7 @@ public class FakeDataSource implements DataSource {
|
||||||
System.arraycopy(current.data, current.bytesRead, buffer, offset, readLength);
|
System.arraycopy(current.data, current.bytesRead, buffer, offset, readLength);
|
||||||
}
|
}
|
||||||
onDataRead(readLength);
|
onDataRead(readLength);
|
||||||
if (transferListener != null) {
|
bytesTransferred(readLength);
|
||||||
transferListener.onBytesTransferred(this, readLength);
|
|
||||||
}
|
|
||||||
bytesRemaining -= readLength;
|
bytesRemaining -= readLength;
|
||||||
current.bytesRead += readLength;
|
current.bytesRead += readLength;
|
||||||
if (current.bytesRead == current.length) {
|
if (current.bytesRead == current.length) {
|
||||||
|
|
@ -191,8 +200,8 @@ public class FakeDataSource implements DataSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void close() throws IOException {
|
public final void close() throws IOException {
|
||||||
Assertions.checkState(opened);
|
Assertions.checkState(openCalled);
|
||||||
opened = false;
|
openCalled = false;
|
||||||
uri = null;
|
uri = null;
|
||||||
if (fakeData != null && currentSegmentIndex < fakeData.getSegments().size()) {
|
if (fakeData != null && currentSegmentIndex < fakeData.getSegments().size()) {
|
||||||
Segment current = fakeData.getSegments().get(currentSegmentIndex);
|
Segment current = fakeData.getSegments().get(currentSegmentIndex);
|
||||||
|
|
@ -200,8 +209,9 @@ public class FakeDataSource implements DataSource {
|
||||||
current.exceptionCleared = true;
|
current.exceptionCleared = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transferListener != null) {
|
if (sourceOpened) {
|
||||||
transferListener.onTransferEnd(this);
|
sourceOpened = false;
|
||||||
|
transferEnded();
|
||||||
}
|
}
|
||||||
fakeData = null;
|
fakeData = null;
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +229,7 @@ public class FakeDataSource implements DataSource {
|
||||||
|
|
||||||
/** Returns whether the data source is currently opened. */
|
/** Returns whether the data source is currently opened. */
|
||||||
public final boolean isOpened() {
|
public final boolean isOpened() {
|
||||||
return opened;
|
return sourceOpened;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onDataRead(int bytesRead) throws IOException {
|
protected void onDataRead(int bytesRead) throws IOException {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue