mirror of
https://github.com/samsonjs/media.git
synced 2026-04-04 11:05:47 +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;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.google.android.exoplayer2.C;
|
||||
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.DataSpec;
|
||||
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.
|
||||
*
|
||||
* <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}.
|
||||
|
|
@ -96,6 +100,8 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
*/
|
||||
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 CONTENT_TYPE = "Content-Type";
|
||||
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 Executor executor;
|
||||
private final Predicate<String> contentTypePredicate;
|
||||
private final TransferListener<? super CronetDataSource> listener;
|
||||
private final int connectTimeoutMs;
|
||||
private final int readTimeoutMs;
|
||||
private final boolean resetTimeoutOnRedirects;
|
||||
|
|
@ -144,41 +149,49 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
|
||||
/**
|
||||
* @param cronetEngine A CronetEngine.
|
||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses.
|
||||
* This may be a direct executor (i.e. executes tasks on the calling thread) in order
|
||||
* to avoid a thread hop from Cronet's internal network thread to the response handling
|
||||
* thread. However, to avoid slowing down overall network performance, care must be taken
|
||||
* to make sure response handling is a fast operation when using a direct executor.
|
||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
|
||||
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
|
||||
* hop from Cronet's internal network thread to the response handling thread. However, to
|
||||
* avoid slowing down overall network performance, care must be taken to make sure response
|
||||
* handling is a fast operation when using a direct executor.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then an {@link InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
* @param listener An optional listener.
|
||||
*/
|
||||
public CronetDataSource(CronetEngine cronetEngine, Executor executor,
|
||||
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener) {
|
||||
public CronetDataSource(
|
||||
CronetEngine cronetEngine,
|
||||
Executor executor,
|
||||
Predicate<String> contentTypePredicate,
|
||||
@Nullable TransferListener<? super DataSource> listener) {
|
||||
this(cronetEngine, executor, contentTypePredicate, listener, DEFAULT_CONNECT_TIMEOUT_MILLIS,
|
||||
DEFAULT_READ_TIMEOUT_MILLIS, false, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cronetEngine A CronetEngine.
|
||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses.
|
||||
* This may be a direct executor (i.e. executes tasks on the calling thread) in order
|
||||
* to avoid a thread hop from Cronet's internal network thread to the response handling
|
||||
* thread. However, to avoid slowing down overall network performance, care must be taken
|
||||
* to make sure response handling is a fast operation when using a direct executor.
|
||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
|
||||
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
|
||||
* hop from Cronet's internal network thread to the response handling thread. However, to
|
||||
* avoid slowing down overall network performance, care must be taken to make sure response
|
||||
* handling is a fast operation when using a direct executor.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then an {@link InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
* @param listener An optional listener.
|
||||
* @param connectTimeoutMs The connection timeout, in milliseconds.
|
||||
* @param readTimeoutMs The read timeout, in milliseconds.
|
||||
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
|
||||
* @param defaultRequestProperties The default request properties to be used.
|
||||
*/
|
||||
public CronetDataSource(CronetEngine cronetEngine, Executor executor,
|
||||
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener,
|
||||
int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects,
|
||||
public CronetDataSource(
|
||||
CronetEngine cronetEngine,
|
||||
Executor executor,
|
||||
Predicate<String> contentTypePredicate,
|
||||
@Nullable TransferListener<? super DataSource> listener,
|
||||
int connectTimeoutMs,
|
||||
int readTimeoutMs,
|
||||
boolean resetTimeoutOnRedirects,
|
||||
RequestProperties defaultRequestProperties) {
|
||||
this(cronetEngine, executor, contentTypePredicate, listener, connectTimeoutMs,
|
||||
readTimeoutMs, resetTimeoutOnRedirects, Clock.DEFAULT, defaultRequestProperties, false);
|
||||
|
|
@ -186,14 +199,14 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
|
||||
/**
|
||||
* @param cronetEngine A CronetEngine.
|
||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses.
|
||||
* This may be a direct executor (i.e. executes tasks on the calling thread) in order
|
||||
* to avoid a thread hop from Cronet's internal network thread to the response handling
|
||||
* thread. However, to avoid slowing down overall network performance, care must be taken
|
||||
* to make sure response handling is a fast operation when using a direct executor.
|
||||
* @param executor The {@link java.util.concurrent.Executor} that will handle responses. This may
|
||||
* be a direct executor (i.e. executes tasks on the calling thread) in order to avoid a thread
|
||||
* hop from Cronet's internal network thread to the response handling thread. However, to
|
||||
* avoid slowing down overall network performance, care must be taken to make sure response
|
||||
* handling is a fast operation when using a direct executor.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then an {@link InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then an {@link InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
* @param listener An optional listener.
|
||||
* @param connectTimeoutMs The connection 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
|
||||
* the redirect url in the "Cookie" header.
|
||||
*/
|
||||
public CronetDataSource(CronetEngine cronetEngine, Executor executor,
|
||||
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener,
|
||||
int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects,
|
||||
RequestProperties defaultRequestProperties, boolean handleSetCookieRequests) {
|
||||
public CronetDataSource(
|
||||
CronetEngine cronetEngine,
|
||||
Executor executor,
|
||||
Predicate<String> contentTypePredicate,
|
||||
@Nullable TransferListener<? super DataSource> listener,
|
||||
int connectTimeoutMs,
|
||||
int readTimeoutMs,
|
||||
boolean resetTimeoutOnRedirects,
|
||||
RequestProperties defaultRequestProperties,
|
||||
boolean handleSetCookieRequests) {
|
||||
this(cronetEngine, executor, contentTypePredicate, listener, connectTimeoutMs,
|
||||
readTimeoutMs, resetTimeoutOnRedirects, Clock.DEFAULT, defaultRequestProperties,
|
||||
handleSetCookieRequests);
|
||||
}
|
||||
|
||||
/* package */ CronetDataSource(CronetEngine cronetEngine, Executor executor,
|
||||
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener,
|
||||
int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects, Clock clock,
|
||||
RequestProperties defaultRequestProperties, boolean handleSetCookieRequests) {
|
||||
/* package */ CronetDataSource(
|
||||
CronetEngine cronetEngine,
|
||||
Executor executor,
|
||||
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.executor = Assertions.checkNotNull(executor);
|
||||
this.contentTypePredicate = contentTypePredicate;
|
||||
this.listener = listener;
|
||||
this.connectTimeoutMs = connectTimeoutMs;
|
||||
this.readTimeoutMs = readTimeoutMs;
|
||||
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
|
||||
|
|
@ -227,6 +254,9 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
this.handleSetCookieRequests = handleSetCookieRequests;
|
||||
requestProperties = new RequestProperties();
|
||||
operation = new ConditionVariable();
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
// HttpDataSource implementation.
|
||||
|
|
@ -324,9 +354,7 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
}
|
||||
|
||||
opened = true;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
|
||||
return bytesRemaining;
|
||||
}
|
||||
|
|
@ -392,9 +420,7 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||
bytesRemaining -= bytesRead;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, bytesRead);
|
||||
}
|
||||
bytesTransferred(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
|
@ -413,107 +439,17 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
finished = false;
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
private UrlRequest.Builder buildRequestBuilder(DataSpec dataSpec) throws IOException {
|
||||
UrlRequest.Builder requestBuilder = cronetEngine.newUrlRequestBuilder(
|
||||
dataSpec.uri.toString(), this, executor).allowDirectExecutor();
|
||||
UrlRequest.Builder requestBuilder =
|
||||
cronetEngine
|
||||
.newUrlRequestBuilder(dataSpec.uri.toString(), urlRequestCallback, executor)
|
||||
.allowDirectExecutor();
|
||||
// Set the headers.
|
||||
boolean isContentTypeHeaderSet = false;
|
||||
if (defaultRequestProperties != null) {
|
||||
|
|
@ -656,4 +592,99 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
|||
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.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
|
@ -33,6 +32,7 @@ import android.net.Uri;
|
|||
import android.os.ConditionVariable;
|
||||
import android.os.SystemClock;
|
||||
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.HttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
|
||||
|
|
@ -87,7 +87,7 @@ public final class CronetDataSourceTest {
|
|||
@Mock private UrlRequest.Builder mockUrlRequestBuilder;
|
||||
@Mock private UrlRequest mockUrlRequest;
|
||||
@Mock private Predicate<String> mockContentTypePredicate;
|
||||
@Mock private TransferListener<CronetDataSource> mockTransferListener;
|
||||
@Mock private TransferListener<DataSource> mockTransferListener;
|
||||
@Mock private Executor mockExecutor;
|
||||
@Mock private NetworkException mockNetworkException;
|
||||
@Mock private CronetEngine mockCronetEngine;
|
||||
|
|
@ -99,18 +99,17 @@ public final class CronetDataSourceTest {
|
|||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
dataSourceUnderTest =
|
||||
spy(
|
||||
new CronetDataSource(
|
||||
mockCronetEngine,
|
||||
mockExecutor,
|
||||
mockContentTypePredicate,
|
||||
mockTransferListener,
|
||||
TEST_CONNECT_TIMEOUT_MS,
|
||||
TEST_READ_TIMEOUT_MS,
|
||||
true, // resetTimeoutOnRedirects
|
||||
Clock.DEFAULT,
|
||||
null,
|
||||
false));
|
||||
new CronetDataSource(
|
||||
mockCronetEngine,
|
||||
mockExecutor,
|
||||
mockContentTypePredicate,
|
||||
mockTransferListener,
|
||||
TEST_CONNECT_TIMEOUT_MS,
|
||||
TEST_READ_TIMEOUT_MS,
|
||||
true, // resetTimeoutOnRedirects
|
||||
Clock.DEFAULT,
|
||||
null,
|
||||
false);
|
||||
when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true);
|
||||
when(mockCronetEngine.newUrlRequestBuilder(
|
||||
anyString(), any(UrlRequest.Callback.class), any(Executor.class)))
|
||||
|
|
@ -172,9 +171,10 @@ public final class CronetDataSourceTest {
|
|||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
// Invoke the callback for the previous request.
|
||||
dataSourceUnderTest.onFailed(
|
||||
dataSourceUnderTest.urlRequestCallback.onFailed(
|
||||
mockUrlRequest, testUrlResponseInfo, mockNetworkException);
|
||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest2, testUrlResponseInfo);
|
||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
||||
mockUrlRequest2, testUrlResponseInfo);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
|
|
@ -601,7 +601,7 @@ public final class CronetDataSourceTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConnectResponseBeforeTimeout() throws InterruptedException {
|
||||
public void testConnectResponseBeforeTimeout() throws Exception {
|
||||
long startTimeMs = SystemClock.elapsedRealtime();
|
||||
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
||||
final CountDownLatch openLatch = new CountDownLatch(1);
|
||||
|
|
@ -625,12 +625,12 @@ public final class CronetDataSourceTest {
|
|||
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||
assertNotCountedDown(openLatch);
|
||||
// The response arrives just in time.
|
||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||
openLatch.await();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirectIncreasesConnectionTimeout() throws InterruptedException {
|
||||
public void testRedirectIncreasesConnectionTimeout() throws Exception {
|
||||
long startTimeMs = SystemClock.elapsedRealtime();
|
||||
final ConditionVariable startCondition = buildUrlRequestStartedCondition();
|
||||
final CountDownLatch timedOutLatch = new CountDownLatch(1);
|
||||
|
|
@ -659,7 +659,7 @@ public final class CronetDataSourceTest {
|
|||
ShadowSystemClock.setCurrentTimeMillis(startTimeMs + TEST_CONNECT_TIMEOUT_MS - 1);
|
||||
assertNotCountedDown(timedOutLatch);
|
||||
// A redirect arrives just in time.
|
||||
dataSourceUnderTest.onRedirectReceived(
|
||||
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
||||
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl1");
|
||||
|
||||
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.
|
||||
assertNotCountedDown(timedOutLatch);
|
||||
// A redirect arrives just in time.
|
||||
dataSourceUnderTest.onRedirectReceived(
|
||||
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
||||
mockUrlRequest, testUrlResponseInfo, "RandomRedirectedUrl2");
|
||||
|
||||
newTimeoutMs = 3 * TEST_CONNECT_TIMEOUT_MS - 2;
|
||||
|
|
@ -700,18 +700,17 @@ public final class CronetDataSourceTest {
|
|||
testRedirectParseAndAttachCookie_dataSourceHandlesSetCookie_andPreservesOriginalRequestHeaders()
|
||||
throws HttpDataSourceException {
|
||||
dataSourceUnderTest =
|
||||
spy(
|
||||
new CronetDataSource(
|
||||
mockCronetEngine,
|
||||
mockExecutor,
|
||||
mockContentTypePredicate,
|
||||
mockTransferListener,
|
||||
TEST_CONNECT_TIMEOUT_MS,
|
||||
TEST_READ_TIMEOUT_MS,
|
||||
true, // resetTimeoutOnRedirects
|
||||
Clock.DEFAULT,
|
||||
null,
|
||||
true));
|
||||
new CronetDataSource(
|
||||
mockCronetEngine,
|
||||
mockExecutor,
|
||||
mockContentTypePredicate,
|
||||
mockTransferListener,
|
||||
TEST_CONNECT_TIMEOUT_MS,
|
||||
TEST_READ_TIMEOUT_MS,
|
||||
true, // resetTimeoutOnRedirects
|
||||
Clock.DEFAULT,
|
||||
null,
|
||||
true);
|
||||
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
||||
|
||||
mockSingleRedirectSuccess();
|
||||
|
|
@ -732,18 +731,17 @@ public final class CronetDataSourceTest {
|
|||
throws HttpDataSourceException {
|
||||
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null);
|
||||
dataSourceUnderTest =
|
||||
spy(
|
||||
new CronetDataSource(
|
||||
mockCronetEngine,
|
||||
mockExecutor,
|
||||
mockContentTypePredicate,
|
||||
mockTransferListener,
|
||||
TEST_CONNECT_TIMEOUT_MS,
|
||||
TEST_READ_TIMEOUT_MS,
|
||||
true, // resetTimeoutOnRedirects
|
||||
Clock.DEFAULT,
|
||||
null,
|
||||
true));
|
||||
new CronetDataSource(
|
||||
mockCronetEngine,
|
||||
mockExecutor,
|
||||
mockContentTypePredicate,
|
||||
mockTransferListener,
|
||||
TEST_CONNECT_TIMEOUT_MS,
|
||||
TEST_READ_TIMEOUT_MS,
|
||||
true, // resetTimeoutOnRedirects
|
||||
Clock.DEFAULT,
|
||||
null,
|
||||
true);
|
||||
dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE);
|
||||
|
||||
mockSingleRedirectSuccess();
|
||||
|
|
@ -772,18 +770,17 @@ public final class CronetDataSourceTest {
|
|||
public void testRedirectNoSetCookieFollowsRedirect_dataSourceHandlesSetCookie()
|
||||
throws HttpDataSourceException {
|
||||
dataSourceUnderTest =
|
||||
spy(
|
||||
new CronetDataSource(
|
||||
mockCronetEngine,
|
||||
mockExecutor,
|
||||
mockContentTypePredicate,
|
||||
mockTransferListener,
|
||||
TEST_CONNECT_TIMEOUT_MS,
|
||||
TEST_READ_TIMEOUT_MS,
|
||||
true, // resetTimeoutOnRedirects
|
||||
Clock.DEFAULT,
|
||||
null,
|
||||
true));
|
||||
new CronetDataSource(
|
||||
mockCronetEngine,
|
||||
mockExecutor,
|
||||
mockContentTypePredicate,
|
||||
mockTransferListener,
|
||||
TEST_CONNECT_TIMEOUT_MS,
|
||||
TEST_READ_TIMEOUT_MS,
|
||||
true, // resetTimeoutOnRedirects
|
||||
Clock.DEFAULT,
|
||||
null,
|
||||
true);
|
||||
mockSingleRedirectSuccess();
|
||||
mockFollowRedirectSuccess();
|
||||
|
||||
|
|
@ -889,7 +886,8 @@ public final class CronetDataSourceTest {
|
|||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
||||
mockUrlRequest, testUrlResponseInfo);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
|
|
@ -902,7 +900,7 @@ public final class CronetDataSourceTest {
|
|||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onRedirectReceived(
|
||||
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
||||
mockUrlRequest,
|
||||
createUrlResponseInfo(307), // statusCode
|
||||
"http://redirect.location.com");
|
||||
|
|
@ -920,12 +918,13 @@ public final class CronetDataSourceTest {
|
|||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
if (!redirectCalled) {
|
||||
redirectCalled = true;
|
||||
dataSourceUnderTest.onRedirectReceived(
|
||||
dataSourceUnderTest.urlRequestCallback.onRedirectReceived(
|
||||
mockUrlRequest,
|
||||
createUrlResponseInfoWithUrl("http://example.com/video", 300),
|
||||
"http://example.com/video/redirect");
|
||||
} else {
|
||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
||||
mockUrlRequest, testUrlResponseInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -939,7 +938,8 @@ public final class CronetDataSourceTest {
|
|||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onResponseStarted(mockUrlRequest, testUrlResponseInfo);
|
||||
dataSourceUnderTest.urlRequestCallback.onResponseStarted(
|
||||
mockUrlRequest, testUrlResponseInfo);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
|
|
@ -952,7 +952,7 @@ public final class CronetDataSourceTest {
|
|||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onFailed(
|
||||
dataSourceUnderTest.urlRequestCallback.onFailed(
|
||||
mockUrlRequest,
|
||||
createUrlResponseInfo(500), // statusCode
|
||||
mockNetworkException);
|
||||
|
|
@ -970,14 +970,15 @@ public final class CronetDataSourceTest {
|
|||
@Override
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
if (positionAndRemaining[1] == 0) {
|
||||
dataSourceUnderTest.onSucceeded(mockUrlRequest, testUrlResponseInfo);
|
||||
dataSourceUnderTest.urlRequestCallback.onSucceeded(
|
||||
mockUrlRequest, testUrlResponseInfo);
|
||||
} else {
|
||||
ByteBuffer inputBuffer = (ByteBuffer) invocation.getArguments()[0];
|
||||
int readLength = Math.min(positionAndRemaining[1], inputBuffer.remaining());
|
||||
inputBuffer.put(buildTestDataBuffer(positionAndRemaining[0], readLength));
|
||||
positionAndRemaining[0] += readLength;
|
||||
positionAndRemaining[1] -= readLength;
|
||||
dataSourceUnderTest.onReadCompleted(
|
||||
dataSourceUnderTest.urlRequestCallback.onReadCompleted(
|
||||
mockUrlRequest, testUrlResponseInfo, inputBuffer);
|
||||
}
|
||||
return null;
|
||||
|
|
@ -992,7 +993,7 @@ public final class CronetDataSourceTest {
|
|||
new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
dataSourceUnderTest.onFailed(
|
||||
dataSourceUnderTest.urlRequestCallback.onFailed(
|
||||
mockUrlRequest,
|
||||
createUrlResponseInfo(500), // statusCode
|
||||
mockNetworkException);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import android.support.annotation.NonNull;
|
|||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
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.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
|
|
@ -42,10 +44,8 @@ import okhttp3.Request;
|
|||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* An {@link HttpDataSource} that delegates to Square's {@link Call.Factory}.
|
||||
*/
|
||||
public class OkHttpDataSource implements HttpDataSource {
|
||||
/** An {@link HttpDataSource} that delegates to Square's {@link Call.Factory}. */
|
||||
public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
||||
|
||||
static {
|
||||
ExoPlayerLibraryInfo.registerModule("goog.exo.okhttp");
|
||||
|
|
@ -58,7 +58,6 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
|
||||
@Nullable private final String userAgent;
|
||||
@Nullable private final Predicate<String> contentTypePredicate;
|
||||
@Nullable private final TransferListener<? super OkHttpDataSource> listener;
|
||||
@Nullable private final CacheControl cacheControl;
|
||||
@Nullable private final RequestProperties defaultRequestProperties;
|
||||
|
||||
|
|
@ -90,13 +89,15 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
* by the source.
|
||||
* @param userAgent An optional User-Agent string.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then a {@link InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then a {@link InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
* @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 TransferListener<? super OkHttpDataSource> listener) {
|
||||
@Nullable TransferListener<? super DataSource> listener) {
|
||||
this(callFactory, userAgent, contentTypePredicate, listener, null, null);
|
||||
}
|
||||
|
||||
|
|
@ -105,24 +106,30 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
* by the source.
|
||||
* @param userAgent An optional User-Agent string.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then a {@link InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then a {@link InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
* @param listener An optional listener.
|
||||
* @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header.
|
||||
* @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 TransferListener<? super OkHttpDataSource> listener,
|
||||
@Nullable CacheControl cacheControl, @Nullable RequestProperties defaultRequestProperties) {
|
||||
@Nullable TransferListener<? super DataSource> listener,
|
||||
@Nullable CacheControl cacheControl,
|
||||
@Nullable RequestProperties defaultRequestProperties) {
|
||||
super(DataSource.TYPE_REMOTE);
|
||||
this.callFactory = Assertions.checkNotNull(callFactory);
|
||||
this.userAgent = userAgent;
|
||||
this.contentTypePredicate = contentTypePredicate;
|
||||
this.listener = listener;
|
||||
this.cacheControl = cacheControl;
|
||||
this.defaultRequestProperties = defaultRequestProperties;
|
||||
this.requestProperties = new RequestProperties();
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -203,9 +210,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
}
|
||||
|
||||
opened = true;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
|
||||
return bytesToRead;
|
||||
}
|
||||
|
|
@ -224,9 +229,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
public void close() throws HttpDataSourceException {
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
closeConnectionQuietly();
|
||||
}
|
||||
}
|
||||
|
|
@ -333,9 +336,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
throw new EOFException();
|
||||
}
|
||||
bytesSkipped += read;
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, read);
|
||||
}
|
||||
bytesTransferred(read);
|
||||
}
|
||||
|
||||
// Release the shared skip buffer.
|
||||
|
|
@ -378,9 +379,7 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||
}
|
||||
|
||||
bytesRead += read;
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, read);
|
||||
}
|
||||
bytesTransferred(read);
|
||||
return read;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import android.net.Uri;
|
|||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
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.DataSpec;
|
||||
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.RtmpIOException;
|
||||
|
||||
/**
|
||||
* A Real-Time Messaging Protocol (RTMP) {@link DataSource}.
|
||||
*/
|
||||
public final class RtmpDataSource implements DataSource {
|
||||
/** A Real-Time Messaging Protocol (RTMP) {@link DataSource}. */
|
||||
public final class RtmpDataSource extends BaseDataSource {
|
||||
|
||||
static {
|
||||
ExoPlayerLibraryInfo.registerModule("goog.exo.rtmp");
|
||||
}
|
||||
|
||||
@Nullable private final TransferListener<? super RtmpDataSource> listener;
|
||||
|
||||
private RtmpClient rtmpClient;
|
||||
private Uri uri;
|
||||
|
||||
|
|
@ -44,11 +41,12 @@ public final class RtmpDataSource implements DataSource {
|
|||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param listener An optional listener.
|
||||
*/
|
||||
public RtmpDataSource(@Nullable TransferListener<? super RtmpDataSource> listener) {
|
||||
this.listener = listener;
|
||||
/** @param listener An optional listener. */
|
||||
public RtmpDataSource(@Nullable TransferListener<? super DataSource> listener) {
|
||||
super(DataSource.TYPE_REMOTE);
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -57,9 +55,7 @@ public final class RtmpDataSource implements DataSource {
|
|||
rtmpClient.open(dataSpec.uri.toString(), false);
|
||||
|
||||
this.uri = dataSpec.uri;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
return C.LENGTH_UNSET;
|
||||
}
|
||||
|
||||
|
|
@ -69,9 +65,7 @@ public final class RtmpDataSource implements DataSource {
|
|||
if (bytesRead == -1) {
|
||||
return C.RESULT_END_OF_INPUT;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, bytesRead);
|
||||
}
|
||||
bytesTransferred(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
|
@ -79,9 +73,7 @@ public final class RtmpDataSource implements DataSource {
|
|||
public void close() {
|
||||
if (uri != null) {
|
||||
uri = null;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
}
|
||||
if (rtmpClient != null) {
|
||||
rtmpClient.close();
|
||||
|
|
|
|||
|
|
@ -25,17 +25,14 @@ import com.google.android.exoplayer2.upstream.TransferListener;
|
|||
*/
|
||||
public final class RtmpDataSourceFactory implements DataSource.Factory {
|
||||
|
||||
@Nullable
|
||||
private final TransferListener<? super RtmpDataSource> listener;
|
||||
private final @Nullable TransferListener<? super DataSource> listener;
|
||||
|
||||
public RtmpDataSourceFactory() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param listener An optional listener.
|
||||
*/
|
||||
public RtmpDataSourceFactory(@Nullable TransferListener<? super RtmpDataSource> listener) {
|
||||
/** @param listener An optional listener. */
|
||||
public RtmpDataSourceFactory(@Nullable TransferListener<? super DataSource> listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,14 @@ package com.google.android.exoplayer2.upstream;
|
|||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A {@link DataSource} for reading from a local asset.
|
||||
*/
|
||||
public final class AssetDataSource implements DataSource {
|
||||
/** A {@link DataSource} for reading from a local asset. */
|
||||
public final class AssetDataSource extends BaseDataSource {
|
||||
|
||||
/**
|
||||
* 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 TransferListener<? super AssetDataSource> listener;
|
||||
|
||||
private Uri uri;
|
||||
private InputStream inputStream;
|
||||
private @Nullable Uri uri;
|
||||
private @Nullable InputStream inputStream;
|
||||
private long bytesRemaining;
|
||||
private boolean opened;
|
||||
|
||||
|
|
@ -58,9 +56,12 @@ public final class AssetDataSource implements DataSource {
|
|||
* @param context A context.
|
||||
* @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.listener = listener;
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -96,9 +97,7 @@ public final class AssetDataSource implements DataSource {
|
|||
}
|
||||
|
||||
opened = true;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
return bytesRemaining;
|
||||
}
|
||||
|
||||
|
|
@ -129,14 +128,12 @@ public final class AssetDataSource implements DataSource {
|
|||
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||
bytesRemaining -= bytesRead;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, bytesRead);
|
||||
}
|
||||
bytesTransferred(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
public @Nullable Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
|
@ -153,9 +150,7 @@ public final class AssetDataSource implements DataSource {
|
|||
inputStream = null;
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,25 +16,26 @@
|
|||
package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A {@link DataSource} for reading from a byte array.
|
||||
*/
|
||||
public final class ByteArrayDataSource implements DataSource {
|
||||
/** A {@link DataSource} for reading from a byte array. */
|
||||
public final class ByteArrayDataSource extends BaseDataSource {
|
||||
|
||||
private final byte[] data;
|
||||
|
||||
private Uri uri;
|
||||
private @Nullable Uri uri;
|
||||
private int readPosition;
|
||||
private int bytesRemaining;
|
||||
private boolean opened;
|
||||
|
||||
/**
|
||||
* @param data The data to be read.
|
||||
*/
|
||||
public ByteArrayDataSource(byte[] data) {
|
||||
super(DataSource.TYPE_LOCAL);
|
||||
Assertions.checkNotNull(data);
|
||||
Assertions.checkArgument(data.length > 0);
|
||||
this.data = data;
|
||||
|
|
@ -50,6 +51,8 @@ public final class ByteArrayDataSource implements DataSource {
|
|||
throw new IOException("Unsatisfiable range: [" + readPosition + ", " + dataSpec.length
|
||||
+ "], length: " + data.length);
|
||||
}
|
||||
opened = true;
|
||||
transferStarted(dataSpec);
|
||||
return bytesRemaining;
|
||||
}
|
||||
|
||||
|
|
@ -65,16 +68,21 @@ public final class ByteArrayDataSource implements DataSource {
|
|||
System.arraycopy(data, readPosition, buffer, offset, readLength);
|
||||
readPosition += readLength;
|
||||
bytesRemaining -= readLength;
|
||||
bytesTransferred(readLength);
|
||||
return readLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
public @Nullable Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (opened) {
|
||||
opened = false;
|
||||
transferEnded();
|
||||
}
|
||||
uri = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import android.content.ContentResolver;
|
|||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import java.io.EOFException;
|
||||
import java.io.FileInputStream;
|
||||
|
|
@ -26,10 +27,8 @@ import java.io.FileNotFoundException;
|
|||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* A {@link DataSource} for reading from a content URI.
|
||||
*/
|
||||
public final class ContentDataSource implements DataSource {
|
||||
/** A {@link DataSource} for reading from a content URI. */
|
||||
public final class ContentDataSource extends BaseDataSource {
|
||||
|
||||
/**
|
||||
* 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 TransferListener<? super ContentDataSource> listener;
|
||||
|
||||
private Uri uri;
|
||||
private AssetFileDescriptor assetFileDescriptor;
|
||||
private FileInputStream inputStream;
|
||||
private @Nullable Uri uri;
|
||||
private @Nullable AssetFileDescriptor assetFileDescriptor;
|
||||
private @Nullable FileInputStream inputStream;
|
||||
private long bytesRemaining;
|
||||
private boolean opened;
|
||||
|
||||
|
|
@ -62,9 +60,13 @@ public final class ContentDataSource implements DataSource {
|
|||
* @param context A context.
|
||||
* @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.listener = listener;
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -102,9 +104,7 @@ public final class ContentDataSource implements DataSource {
|
|||
}
|
||||
|
||||
opened = true;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
|
||||
return bytesRemaining;
|
||||
}
|
||||
|
|
@ -136,14 +136,12 @@ public final class ContentDataSource implements DataSource {
|
|||
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||
bytesRemaining -= bytesRead;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, bytesRead);
|
||||
}
|
||||
bytesTransferred(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
public @Nullable Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
|
@ -168,9 +166,7 @@ public final class ContentDataSource implements DataSource {
|
|||
assetFileDescriptor = null;
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Base64;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
|
|
@ -23,16 +24,18 @@ import com.google.android.exoplayer2.util.Util;
|
|||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
/**
|
||||
* A {@link DataSource} for reading data URLs, as defined by RFC 2397.
|
||||
*/
|
||||
public final class DataSchemeDataSource implements DataSource {
|
||||
/** A {@link DataSource} for reading data URLs, as defined by RFC 2397. */
|
||||
public final class DataSchemeDataSource extends BaseDataSource {
|
||||
|
||||
public static final String SCHEME_DATA = "data";
|
||||
|
||||
private DataSpec dataSpec;
|
||||
private @Nullable DataSpec dataSpec;
|
||||
private int bytesRead;
|
||||
private byte[] data;
|
||||
private @Nullable byte[] data;
|
||||
|
||||
public DataSchemeDataSource() {
|
||||
super(DataSource.TYPE_LOCAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long open(DataSpec dataSpec) throws IOException {
|
||||
|
|
@ -57,6 +60,7 @@ public final class DataSchemeDataSource implements DataSource {
|
|||
// TODO: Add support for other charsets.
|
||||
data = URLDecoder.decode(dataString, C.ASCII_NAME).getBytes();
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
return data.length;
|
||||
}
|
||||
|
||||
|
|
@ -72,18 +76,22 @@ public final class DataSchemeDataSource implements DataSource {
|
|||
readLength = Math.min(readLength, remainingBytes);
|
||||
System.arraycopy(data, bytesRead, buffer, offset, readLength);
|
||||
bytesRead += readLength;
|
||||
bytesTransferred(readLength);
|
||||
return readLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
public @Nullable Uri getUri() {
|
||||
return dataSpec != null ? dataSpec.uri : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (data != null) {
|
||||
data = null;
|
||||
transferEnded();
|
||||
}
|
||||
dataSpec = null;
|
||||
data = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.google.android.exoplayer2.C;
|
||||
|
|
@ -41,13 +42,13 @@ import java.util.regex.Pattern;
|
|||
|
||||
/**
|
||||
* An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}.
|
||||
* <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
|
||||
* {@link #DefaultHttpDataSource(String, Predicate, TransferListener, int, int, boolean,
|
||||
*
|
||||
* <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 {@link
|
||||
* #DefaultHttpDataSource(String, Predicate, TransferListener, int, int, boolean,
|
||||
* 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.
|
||||
|
|
@ -69,14 +70,13 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
|||
private final int connectTimeoutMillis;
|
||||
private final int readTimeoutMillis;
|
||||
private final String userAgent;
|
||||
private final Predicate<String> contentTypePredicate;
|
||||
private final RequestProperties defaultRequestProperties;
|
||||
private final @Nullable Predicate<String> contentTypePredicate;
|
||||
private final @Nullable RequestProperties defaultRequestProperties;
|
||||
private final RequestProperties requestProperties;
|
||||
private final TransferListener<? super DefaultHttpDataSource> listener;
|
||||
|
||||
private DataSpec dataSpec;
|
||||
private HttpURLConnection connection;
|
||||
private InputStream inputStream;
|
||||
private @Nullable DataSpec dataSpec;
|
||||
private @Nullable HttpURLConnection connection;
|
||||
private @Nullable InputStream inputStream;
|
||||
private boolean opened;
|
||||
|
||||
private long bytesToSkip;
|
||||
|
|
@ -88,22 +88,24 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
|||
/**
|
||||
* @param userAgent The User-Agent string that should be used.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
*/
|
||||
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate) {
|
||||
public DefaultHttpDataSource(String userAgent, @Nullable Predicate<String> contentTypePredicate) {
|
||||
this(userAgent, contentTypePredicate, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userAgent The User-Agent string that should be used.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
* @param listener An optional listener.
|
||||
*/
|
||||
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
|
||||
TransferListener<? super DefaultHttpDataSource> listener) {
|
||||
public DefaultHttpDataSource(
|
||||
String userAgent,
|
||||
@Nullable Predicate<String> contentTypePredicate,
|
||||
@Nullable TransferListener<? super DataSource> listener) {
|
||||
this(userAgent, contentTypePredicate, listener, DEFAULT_CONNECT_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 contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
* @param listener An optional listener.
|
||||
* @param connectTimeoutMillis The connection timeout, in milliseconds. A timeout of zero is
|
||||
* interpreted as an infinite timeout.
|
||||
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted
|
||||
* as an infinite timeout.
|
||||
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as
|
||||
* an infinite timeout.
|
||||
*/
|
||||
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
|
||||
TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis,
|
||||
public DefaultHttpDataSource(
|
||||
String userAgent,
|
||||
@Nullable Predicate<String> contentTypePredicate,
|
||||
@Nullable TransferListener<? super DataSource> listener,
|
||||
int connectTimeoutMillis,
|
||||
int readTimeoutMillis) {
|
||||
this(userAgent, contentTypePredicate, listener, connectTimeoutMillis, readTimeoutMillis, false,
|
||||
null);
|
||||
|
|
@ -129,35 +134,42 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
|||
/**
|
||||
* @param userAgent The User-Agent string that should be used.
|
||||
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
|
||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from
|
||||
* {@link #open(DataSpec)}.
|
||||
* predicate then a {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
|
||||
* #open(DataSpec)}.
|
||||
* @param listener An optional listener.
|
||||
* @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
|
||||
* the default value.
|
||||
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted
|
||||
* as an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value.
|
||||
* interpreted as an infinite timeout. Pass {@link #DEFAULT_CONNECT_TIMEOUT_MILLIS} to use the
|
||||
* default value.
|
||||
* @param readTimeoutMillis The read timeout, in milliseconds. A timeout of zero is interpreted as
|
||||
* 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
|
||||
* to HTTPS and vice versa) are enabled.
|
||||
* @param defaultRequestProperties The default request properties to be sent to the server as
|
||||
* HTTP headers or {@code null} if not required.
|
||||
* @param defaultRequestProperties The default request properties to be sent to the server as HTTP
|
||||
* headers or {@code null} if not required.
|
||||
*/
|
||||
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
|
||||
TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis,
|
||||
int readTimeoutMillis, boolean allowCrossProtocolRedirects,
|
||||
RequestProperties defaultRequestProperties) {
|
||||
public DefaultHttpDataSource(
|
||||
String userAgent,
|
||||
@Nullable Predicate<String> contentTypePredicate,
|
||||
@Nullable TransferListener<? super DataSource> listener,
|
||||
int connectTimeoutMillis,
|
||||
int readTimeoutMillis,
|
||||
boolean allowCrossProtocolRedirects,
|
||||
@Nullable RequestProperties defaultRequestProperties) {
|
||||
super(DataSource.TYPE_REMOTE);
|
||||
this.userAgent = Assertions.checkNotEmpty(userAgent);
|
||||
this.contentTypePredicate = contentTypePredicate;
|
||||
this.listener = listener;
|
||||
this.requestProperties = new RequestProperties();
|
||||
this.connectTimeoutMillis = connectTimeoutMillis;
|
||||
this.readTimeoutMillis = readTimeoutMillis;
|
||||
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
|
||||
this.defaultRequestProperties = defaultRequestProperties;
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
public @Nullable Uri getUri() {
|
||||
return connection == null ? null : Uri.parse(connection.getURL().toString());
|
||||
}
|
||||
|
||||
|
|
@ -254,9 +266,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
|||
}
|
||||
|
||||
opened = true;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
|
||||
return bytesToRead;
|
||||
}
|
||||
|
|
@ -287,9 +297,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
|||
closeConnectionQuietly();
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -534,9 +542,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
|||
throw new EOFException();
|
||||
}
|
||||
bytesSkipped += read;
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, read);
|
||||
}
|
||||
bytesTransferred(read);
|
||||
}
|
||||
|
||||
// Release the shared skip buffer.
|
||||
|
|
@ -579,9 +585,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
|||
}
|
||||
|
||||
bytesRead += read;
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, read);
|
||||
}
|
||||
bytesTransferred(read);
|
||||
return read;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,15 +16,14 @@
|
|||
package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
/**
|
||||
* A {@link DataSource} for reading local files.
|
||||
*/
|
||||
public final class FileDataSource implements DataSource {
|
||||
/** A {@link DataSource} for reading local files. */
|
||||
public final class FileDataSource extends BaseDataSource {
|
||||
|
||||
/**
|
||||
* 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 RandomAccessFile file;
|
||||
private Uri uri;
|
||||
private @Nullable RandomAccessFile file;
|
||||
private @Nullable Uri uri;
|
||||
private long bytesRemaining;
|
||||
private boolean opened;
|
||||
|
||||
|
|
@ -48,11 +45,12 @@ public final class FileDataSource implements DataSource {
|
|||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param listener An optional listener.
|
||||
*/
|
||||
public FileDataSource(TransferListener<? super FileDataSource> listener) {
|
||||
this.listener = listener;
|
||||
/** @param listener An optional listener. */
|
||||
public FileDataSource(@Nullable TransferListener<? super DataSource> listener) {
|
||||
super(DataSource.TYPE_LOCAL);
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -71,9 +69,7 @@ public final class FileDataSource implements DataSource {
|
|||
}
|
||||
|
||||
opened = true;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
|
||||
return bytesRemaining;
|
||||
}
|
||||
|
|
@ -94,9 +90,7 @@ public final class FileDataSource implements DataSource {
|
|||
|
||||
if (bytesRead > 0) {
|
||||
bytesRemaining -= bytesRead;
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, bytesRead);
|
||||
}
|
||||
bytesTransferred(bytesRead);
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
|
|
@ -104,7 +98,7 @@ public final class FileDataSource implements DataSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
public @Nullable Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
|
@ -121,9 +115,7 @@ public final class FileDataSource implements DataSource {
|
|||
file = null;
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,18 +15,20 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link DataSource.Factory} that produces {@link FileDataSource}.
|
||||
*/
|
||||
public final class FileDataSourceFactory implements DataSource.Factory {
|
||||
|
||||
private final TransferListener<? super FileDataSource> listener;
|
||||
private final @Nullable TransferListener<? super DataSource> listener;
|
||||
|
||||
public FileDataSourceFactory() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public FileDataSourceFactory(TransferListener<? super FileDataSource> listener) {
|
||||
public FileDataSourceFactory(@Nullable TransferListener<? super DataSource> listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import android.content.Context;
|
|||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import java.io.EOFException;
|
||||
|
|
@ -28,12 +29,12 @@ import java.io.InputStream;
|
|||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
|
|
@ -62,11 +63,10 @@ public final class RawResourceDataSource implements DataSource {
|
|||
public static final String RAW_RESOURCE_SCHEME = "rawresource";
|
||||
|
||||
private final Resources resources;
|
||||
private final TransferListener<? super RawResourceDataSource> listener;
|
||||
|
||||
private Uri uri;
|
||||
private AssetFileDescriptor assetFileDescriptor;
|
||||
private InputStream inputStream;
|
||||
private @Nullable Uri uri;
|
||||
private @Nullable AssetFileDescriptor assetFileDescriptor;
|
||||
private @Nullable InputStream inputStream;
|
||||
private long bytesRemaining;
|
||||
private boolean opened;
|
||||
|
||||
|
|
@ -81,10 +81,13 @@ public final class RawResourceDataSource implements DataSource {
|
|||
* @param context A context.
|
||||
* @param listener An optional listener.
|
||||
*/
|
||||
public RawResourceDataSource(Context context,
|
||||
TransferListener<? super RawResourceDataSource> listener) {
|
||||
public RawResourceDataSource(
|
||||
Context context, @Nullable TransferListener<? super DataSource> listener) {
|
||||
super(DataSource.TYPE_LOCAL);
|
||||
this.resources = context.getResources();
|
||||
this.listener = listener;
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -124,9 +127,7 @@ public final class RawResourceDataSource implements DataSource {
|
|||
}
|
||||
|
||||
opened = true;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
|
||||
return bytesRemaining;
|
||||
}
|
||||
|
|
@ -158,14 +159,12 @@ public final class RawResourceDataSource implements DataSource {
|
|||
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||
bytesRemaining -= bytesRead;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, bytesRead);
|
||||
}
|
||||
bytesTransferred(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
public @Nullable Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
|
@ -190,9 +189,7 @@ public final class RawResourceDataSource implements DataSource {
|
|||
assetFileDescriptor = null;
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
|
|
@ -25,10 +26,8 @@ import java.net.InetSocketAddress;
|
|||
import java.net.MulticastSocket;
|
||||
import java.net.SocketException;
|
||||
|
||||
/**
|
||||
* A UDP {@link DataSource}.
|
||||
*/
|
||||
public final class UdpDataSource implements DataSource {
|
||||
/** A UDP {@link DataSource}. */
|
||||
public final class UdpDataSource extends BaseDataSource {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
private final TransferListener<? super UdpDataSource> listener;
|
||||
private final int socketTimeoutMillis;
|
||||
private final byte[] packetBuffer;
|
||||
private final DatagramPacket packet;
|
||||
|
||||
private Uri uri;
|
||||
private DatagramSocket socket;
|
||||
private MulticastSocket multicastSocket;
|
||||
private InetAddress address;
|
||||
private InetSocketAddress socketAddress;
|
||||
private @Nullable Uri uri;
|
||||
private @Nullable DatagramSocket socket;
|
||||
private @Nullable MulticastSocket multicastSocket;
|
||||
private @Nullable InetAddress address;
|
||||
private @Nullable InetSocketAddress socketAddress;
|
||||
private boolean opened;
|
||||
|
||||
private int packetRemaining;
|
||||
|
||||
/**
|
||||
* @param listener An optional listener.
|
||||
*/
|
||||
public UdpDataSource(TransferListener<? super UdpDataSource> listener) {
|
||||
/** @param listener An optional listener. */
|
||||
public UdpDataSource(@Nullable TransferListener<? super DataSource> listener) {
|
||||
this(listener, DEFAULT_MAX_PACKET_SIZE);
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +72,7 @@ public final class UdpDataSource implements DataSource {
|
|||
* @param listener An optional listener.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
|
@ -86,12 +82,17 @@ public final class UdpDataSource implements DataSource {
|
|||
* @param socketTimeoutMillis The socket timeout in milliseconds. A timeout of zero is interpreted
|
||||
* as an infinite timeout.
|
||||
*/
|
||||
public UdpDataSource(TransferListener<? super UdpDataSource> listener, int maxPacketSize,
|
||||
public UdpDataSource(
|
||||
@Nullable TransferListener<? super DataSource> listener,
|
||||
int maxPacketSize,
|
||||
int socketTimeoutMillis) {
|
||||
this.listener = listener;
|
||||
super(DataSource.TYPE_REMOTE);
|
||||
this.socketTimeoutMillis = socketTimeoutMillis;
|
||||
packetBuffer = new byte[maxPacketSize];
|
||||
packet = new DatagramPacket(packetBuffer, 0, maxPacketSize);
|
||||
if (listener != null) {
|
||||
addTransferListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -121,9 +122,7 @@ public final class UdpDataSource implements DataSource {
|
|||
}
|
||||
|
||||
opened = true;
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
transferStarted(dataSpec);
|
||||
return C.LENGTH_UNSET;
|
||||
}
|
||||
|
||||
|
|
@ -141,9 +140,7 @@ public final class UdpDataSource implements DataSource {
|
|||
throw new UdpDataSourceException(e);
|
||||
}
|
||||
packetRemaining = packet.getLength();
|
||||
if (listener != null) {
|
||||
listener.onBytesTransferred(this, packetRemaining);
|
||||
}
|
||||
bytesTransferred(packetRemaining);
|
||||
}
|
||||
|
||||
int packetOffset = packet.getLength() - packetRemaining;
|
||||
|
|
@ -154,7 +151,7 @@ public final class UdpDataSource implements DataSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
public @Nullable Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
|
@ -178,9 +175,7 @@ public final class UdpDataSource implements DataSource {
|
|||
packetRemaining = 0;
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (listener != null) {
|
||||
listener.onTransferEnd(this);
|
||||
}
|
||||
transferEnded();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import android.support.annotation.Nullable;
|
|||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData;
|
||||
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.DataSourceException;
|
||||
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}
|
||||
* 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}.
|
||||
*/
|
||||
public static class Factory implements DataSource.Factory {
|
||||
|
||||
protected final TransferListener<? super FakeDataSource> transferListener;
|
||||
protected final TransferListener<? super DataSource> transferListener;
|
||||
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.dataSourceType = DataSource.TYPE_LOCAL;
|
||||
}
|
||||
|
||||
public final Factory setFakeDataSet(FakeDataSet fakeDataSet) {
|
||||
|
|
@ -51,19 +54,23 @@ public class FakeDataSource implements DataSource {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource createDataSource() {
|
||||
return new FakeDataSource(fakeDataSet, transferListener);
|
||||
public final Factory setDataSourceType(@DataSource.Type int dataSourceType) {
|
||||
this.dataSourceType = dataSourceType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource createDataSource() {
|
||||
return new FakeDataSource(fakeDataSet, transferListener, dataSourceType);
|
||||
}
|
||||
}
|
||||
|
||||
private final FakeDataSet fakeDataSet;
|
||||
private final TransferListener<? super FakeDataSource> transferListener;
|
||||
private final ArrayList<DataSpec> openedDataSpecs;
|
||||
|
||||
private Uri uri;
|
||||
private boolean opened;
|
||||
private boolean openCalled;
|
||||
private boolean sourceOpened;
|
||||
private FakeData fakeData;
|
||||
private int currentSegmentIndex;
|
||||
private long bytesRemaining;
|
||||
|
|
@ -73,15 +80,20 @@ public class FakeDataSource implements DataSource {
|
|||
}
|
||||
|
||||
public FakeDataSource(FakeDataSet fakeDataSet) {
|
||||
this(fakeDataSet, null);
|
||||
this(fakeDataSet, null, DataSource.TYPE_LOCAL);
|
||||
}
|
||||
|
||||
public FakeDataSource(FakeDataSet fakeDataSet,
|
||||
@Nullable TransferListener<? super FakeDataSource> transferListener) {
|
||||
public FakeDataSource(
|
||||
FakeDataSet fakeDataSet,
|
||||
@Nullable TransferListener<? super DataSource> transferListener,
|
||||
@DataSource.Type int dataSourceType) {
|
||||
super(dataSourceType);
|
||||
Assertions.checkNotNull(fakeDataSet);
|
||||
this.fakeDataSet = fakeDataSet;
|
||||
this.transferListener = transferListener;
|
||||
this.openedDataSpecs = new ArrayList<>();
|
||||
if (transferListener != null) {
|
||||
addTransferListener(transferListener);
|
||||
}
|
||||
}
|
||||
|
||||
public final FakeDataSet getDataSet() {
|
||||
|
|
@ -90,9 +102,9 @@ public class FakeDataSource implements DataSource {
|
|||
|
||||
@Override
|
||||
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.
|
||||
opened = true;
|
||||
uri = dataSpec.uri;
|
||||
openedDataSpecs.add(dataSpec);
|
||||
|
||||
|
|
@ -129,9 +141,8 @@ public class FakeDataSource implements DataSource {
|
|||
currentSegmentIndex++;
|
||||
}
|
||||
}
|
||||
if (transferListener != null) {
|
||||
transferListener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
sourceOpened = true;
|
||||
transferStarted(dataSpec);
|
||||
// Configure bytesRemaining, and return.
|
||||
if (dataSpec.length == C.LENGTH_UNSET) {
|
||||
bytesRemaining = totalLength - dataSpec.position;
|
||||
|
|
@ -144,7 +155,7 @@ public class FakeDataSource implements DataSource {
|
|||
|
||||
@Override
|
||||
public final int read(byte[] buffer, int offset, int readLength) throws IOException {
|
||||
Assertions.checkState(opened);
|
||||
Assertions.checkState(sourceOpened);
|
||||
while (true) {
|
||||
if (currentSegmentIndex == fakeData.getSegments().size() || bytesRemaining == 0) {
|
||||
return C.RESULT_END_OF_INPUT;
|
||||
|
|
@ -171,9 +182,7 @@ public class FakeDataSource implements DataSource {
|
|||
System.arraycopy(current.data, current.bytesRead, buffer, offset, readLength);
|
||||
}
|
||||
onDataRead(readLength);
|
||||
if (transferListener != null) {
|
||||
transferListener.onBytesTransferred(this, readLength);
|
||||
}
|
||||
bytesTransferred(readLength);
|
||||
bytesRemaining -= readLength;
|
||||
current.bytesRead += readLength;
|
||||
if (current.bytesRead == current.length) {
|
||||
|
|
@ -191,8 +200,8 @@ public class FakeDataSource implements DataSource {
|
|||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
Assertions.checkState(opened);
|
||||
opened = false;
|
||||
Assertions.checkState(openCalled);
|
||||
openCalled = false;
|
||||
uri = null;
|
||||
if (fakeData != null && currentSegmentIndex < fakeData.getSegments().size()) {
|
||||
Segment current = fakeData.getSegments().get(currentSegmentIndex);
|
||||
|
|
@ -200,8 +209,9 @@ public class FakeDataSource implements DataSource {
|
|||
current.exceptionCleared = true;
|
||||
}
|
||||
}
|
||||
if (transferListener != null) {
|
||||
transferListener.onTransferEnd(this);
|
||||
if (sourceOpened) {
|
||||
sourceOpened = false;
|
||||
transferEnded();
|
||||
}
|
||||
fakeData = null;
|
||||
}
|
||||
|
|
@ -219,7 +229,7 @@ public class FakeDataSource implements DataSource {
|
|||
|
||||
/** Returns whether the data source is currently opened. */
|
||||
public final boolean isOpened() {
|
||||
return opened;
|
||||
return sourceOpened;
|
||||
}
|
||||
|
||||
protected void onDataRead(int bytesRead) throws IOException {
|
||||
|
|
|
|||
Loading…
Reference in a new issue