use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class DefaultHttpDataSource method makeConnection.
/**
* Establishes a connection, following redirects to do so where permitted.
*/
private HttpURLConnection makeConnection(DataSpec dataSpec) throws IOException {
URL url = new URL(dataSpec.uri.toString());
@HttpMethod int httpMethod = dataSpec.httpMethod;
@Nullable byte[] httpBody = dataSpec.httpBody;
long position = dataSpec.position;
long length = dataSpec.length;
boolean allowGzip = dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_GZIP);
if (!allowCrossProtocolRedirects && !keepPostFor302Redirects) {
// automatically. This is the behavior we want, so use it.
return makeConnection(url, httpMethod, httpBody, position, length, allowGzip, /* followRedirects= */
true, dataSpec.httpRequestHeaders);
}
// We need to handle redirects ourselves to allow cross-protocol redirects or to keep the POST
// request method for 302.
int redirectCount = 0;
while (redirectCount++ <= MAX_REDIRECTS) {
HttpURLConnection connection = makeConnection(url, httpMethod, httpBody, position, length, allowGzip, /* followRedirects= */
false, dataSpec.httpRequestHeaders);
int responseCode = connection.getResponseCode();
String location = connection.getHeaderField("Location");
if ((httpMethod == DataSpec.HTTP_METHOD_GET || httpMethod == DataSpec.HTTP_METHOD_HEAD) && (responseCode == HttpURLConnection.HTTP_MULT_CHOICE || responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_SEE_OTHER || responseCode == HTTP_STATUS_TEMPORARY_REDIRECT || responseCode == HTTP_STATUS_PERMANENT_REDIRECT)) {
connection.disconnect();
url = handleRedirect(url, location, dataSpec);
} else if (httpMethod == DataSpec.HTTP_METHOD_POST && (responseCode == HttpURLConnection.HTTP_MULT_CHOICE || responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_SEE_OTHER)) {
connection.disconnect();
boolean shouldKeepPost = keepPostFor302Redirects && responseCode == HttpURLConnection.HTTP_MOVED_TEMP;
if (!shouldKeepPost) {
// POST request follows the redirect and is transformed into a GET request.
httpMethod = DataSpec.HTTP_METHOD_GET;
httpBody = null;
}
url = handleRedirect(url, location, dataSpec);
} else {
return connection;
}
}
// If we get here we've been redirected more times than are permitted.
throw new HttpDataSourceException(new NoRouteToHostException("Too many redirects: " + redirectCount), dataSpec, PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED, HttpDataSourceException.TYPE_OPEN);
}
use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class DashDownloaderTest method progressiveDownloadSeparatePeriods.
@Test
public void progressiveDownloadSeparatePeriods() throws Exception {
FakeDataSet fakeDataSet = new FakeDataSet().setData(TEST_MPD_URI, TEST_MPD).setRandomData("audio_init_data", 10).setRandomData("audio_segment_1", 4).setRandomData("audio_segment_2", 5).setRandomData("audio_segment_3", 6).setRandomData("period_2_segment_1", 1).setRandomData("period_2_segment_2", 2).setRandomData("period_2_segment_3", 3);
FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet);
FakeDataSource.Factory factory = mock(FakeDataSource.Factory.class);
when(factory.createDataSource()).thenReturn(fakeDataSource);
DashDownloader dashDownloader = getDashDownloader(factory, new StreamKey(0, 0, 0), new StreamKey(1, 0, 0));
dashDownloader.download(progressListener);
DataSpec[] openedDataSpecs = fakeDataSource.getAndClearOpenedDataSpecs();
assertThat(openedDataSpecs.length).isEqualTo(8);
assertThat(openedDataSpecs[0].uri).isEqualTo(TEST_MPD_URI);
assertThat(openedDataSpecs[1].uri.getPath()).isEqualTo("audio_init_data");
assertThat(openedDataSpecs[2].uri.getPath()).isEqualTo("audio_segment_1");
assertThat(openedDataSpecs[3].uri.getPath()).isEqualTo("audio_segment_2");
assertThat(openedDataSpecs[4].uri.getPath()).isEqualTo("audio_segment_3");
assertThat(openedDataSpecs[5].uri.getPath()).isEqualTo("period_2_segment_1");
assertThat(openedDataSpecs[6].uri.getPath()).isEqualTo("period_2_segment_2");
assertThat(openedDataSpecs[7].uri.getPath()).isEqualTo("period_2_segment_3");
}
use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class DashDownloaderTest method progressiveDownload.
@Test
public void progressiveDownload() throws Exception {
FakeDataSet fakeDataSet = new FakeDataSet().setData(TEST_MPD_URI, TEST_MPD).setRandomData("audio_init_data", 10).setRandomData("audio_segment_1", 4).setRandomData("audio_segment_2", 5).setRandomData("audio_segment_3", 6).setRandomData("text_segment_1", 1).setRandomData("text_segment_2", 2).setRandomData("text_segment_3", 3);
FakeDataSource fakeDataSource = new FakeDataSource(fakeDataSet);
FakeDataSource.Factory factory = mock(FakeDataSource.Factory.class);
when(factory.createDataSource()).thenReturn(fakeDataSource);
DashDownloader dashDownloader = getDashDownloader(factory, new StreamKey(0, 0, 0), new StreamKey(0, 1, 0));
dashDownloader.download(progressListener);
DataSpec[] openedDataSpecs = fakeDataSource.getAndClearOpenedDataSpecs();
assertThat(openedDataSpecs.length).isEqualTo(8);
assertThat(openedDataSpecs[0].uri).isEqualTo(TEST_MPD_URI);
assertThat(openedDataSpecs[1].uri.getPath()).isEqualTo("audio_init_data");
assertThat(openedDataSpecs[2].uri.getPath()).isEqualTo("audio_segment_1");
assertThat(openedDataSpecs[3].uri.getPath()).isEqualTo("text_segment_1");
assertThat(openedDataSpecs[4].uri.getPath()).isEqualTo("audio_segment_2");
assertThat(openedDataSpecs[5].uri.getPath()).isEqualTo("text_segment_2");
assertThat(openedDataSpecs[6].uri.getPath()).isEqualTo("audio_segment_3");
assertThat(openedDataSpecs[7].uri.getPath()).isEqualTo("text_segment_3");
}
use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class CacheDataSource method openNextSource.
/**
* Opens the next source. If the cache contains data spanning the current read position then
* {@link #cacheReadDataSource} is opened to read from it. Else {@link #upstreamDataSource} is
* opened to read from the upstream source and write into the cache.
*
* <p>There must not be a currently open source when this method is called, except in the case
* that {@code checkCache} is true. If {@code checkCache} is true then there must be a currently
* open source, and it must be {@link #upstreamDataSource}. It will be closed and a new source
* opened if it's possible to switch to reading from or writing to the cache. If a switch isn't
* possible then the current source is left unchanged.
*
* @param requestDataSpec The original {@link DataSpec} to build upon for the next source.
* @param checkCache If true tries to switch to reading from or writing to cache instead of
* reading from {@link #upstreamDataSource}, which is the currently open source.
*/
private void openNextSource(DataSpec requestDataSpec, boolean checkCache) throws IOException {
@Nullable CacheSpan nextSpan;
String key = castNonNull(requestDataSpec.key);
if (currentRequestIgnoresCache) {
nextSpan = null;
} else if (blockOnCache) {
try {
nextSpan = cache.startReadWrite(key, readPosition, bytesRemaining);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new InterruptedIOException();
}
} else {
nextSpan = cache.startReadWriteNonBlocking(key, readPosition, bytesRemaining);
}
DataSpec nextDataSpec;
DataSource nextDataSource;
if (nextSpan == null) {
// The data is locked in the cache, or we're ignoring the cache. Bypass the cache and read
// from upstream.
nextDataSource = upstreamDataSource;
nextDataSpec = requestDataSpec.buildUpon().setPosition(readPosition).setLength(bytesRemaining).build();
} else if (nextSpan.isCached) {
// Data is cached in a span file starting at nextSpan.position.
Uri fileUri = Uri.fromFile(castNonNull(nextSpan.file));
long filePositionOffset = nextSpan.position;
long positionInFile = readPosition - filePositionOffset;
long length = nextSpan.length - positionInFile;
if (bytesRemaining != C.LENGTH_UNSET) {
length = min(length, bytesRemaining);
}
nextDataSpec = requestDataSpec.buildUpon().setUri(fileUri).setUriPositionOffset(filePositionOffset).setPosition(positionInFile).setLength(length).build();
nextDataSource = cacheReadDataSource;
} else {
// Data is not cached, and data is not locked, read from upstream with cache backing.
long length;
if (nextSpan.isOpenEnded()) {
length = bytesRemaining;
} else {
length = nextSpan.length;
if (bytesRemaining != C.LENGTH_UNSET) {
length = min(length, bytesRemaining);
}
}
nextDataSpec = requestDataSpec.buildUpon().setPosition(readPosition).setLength(length).build();
if (cacheWriteDataSource != null) {
nextDataSource = cacheWriteDataSource;
} else {
nextDataSource = upstreamDataSource;
cache.releaseHoleSpan(nextSpan);
nextSpan = null;
}
}
checkCachePosition = !currentRequestIgnoresCache && nextDataSource == upstreamDataSource ? readPosition + MIN_READ_BEFORE_CHECKING_CACHE : Long.MAX_VALUE;
if (checkCache) {
Assertions.checkState(isBypassingCache());
if (nextDataSource == upstreamDataSource) {
// Continue reading from upstream.
return;
}
// We're switching to reading from or writing to the cache.
try {
closeCurrentSource();
} catch (Throwable e) {
if (castNonNull(nextSpan).isHoleSpan()) {
// Release the hole span before throwing, else we'll hold it forever.
cache.releaseHoleSpan(nextSpan);
}
throw e;
}
}
if (nextSpan != null && nextSpan.isHoleSpan()) {
currentHoleSpan = nextSpan;
}
currentDataSource = nextDataSource;
currentDataSpec = nextDataSpec;
currentDataSourceBytesRead = 0;
long resolvedLength = nextDataSource.open(nextDataSpec);
// Update bytesRemaining, actualUri and (if writing to cache) the cache metadata.
ContentMetadataMutations mutations = new ContentMetadataMutations();
if (nextDataSpec.length == C.LENGTH_UNSET && resolvedLength != C.LENGTH_UNSET) {
bytesRemaining = resolvedLength;
ContentMetadataMutations.setContentLength(mutations, readPosition + bytesRemaining);
}
if (isReadingFromUpstream()) {
actualUri = nextDataSource.getUri();
boolean isRedirected = !requestDataSpec.uri.equals(actualUri);
ContentMetadataMutations.setRedirectedUri(mutations, isRedirected ? actualUri : null);
}
if (isWritingToCache()) {
cache.applyContentMetadataMutations(key, mutations);
}
}
use of com.google.android.exoplayer2.upstream.DataSpec in project ExoPlayer by google.
the class CacheDataSource method open.
@Override
public long open(DataSpec dataSpec) throws IOException {
try {
String key = cacheKeyFactory.buildCacheKey(dataSpec);
DataSpec requestDataSpec = dataSpec.buildUpon().setKey(key).build();
this.requestDataSpec = requestDataSpec;
actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */
requestDataSpec.uri);
readPosition = dataSpec.position;
int reason = shouldIgnoreCacheForRequest(dataSpec);
currentRequestIgnoresCache = reason != CACHE_NOT_IGNORED;
if (currentRequestIgnoresCache) {
notifyCacheIgnored(reason);
}
if (currentRequestIgnoresCache) {
bytesRemaining = C.LENGTH_UNSET;
} else {
bytesRemaining = ContentMetadata.getContentLength(cache.getContentMetadata(key));
if (bytesRemaining != C.LENGTH_UNSET) {
bytesRemaining -= dataSpec.position;
if (bytesRemaining < 0) {
throw new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
}
}
}
if (dataSpec.length != C.LENGTH_UNSET) {
bytesRemaining = bytesRemaining == C.LENGTH_UNSET ? dataSpec.length : min(bytesRemaining, dataSpec.length);
}
if (bytesRemaining > 0 || bytesRemaining == C.LENGTH_UNSET) {
openNextSource(requestDataSpec, false);
}
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : bytesRemaining;
} catch (Throwable e) {
handleBeforeThrow(e);
throw e;
}
}
Aggregations