mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
DataSourceException: Used to specify a DataSource error.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=132053698
This commit is contained in:
parent
884bcb649e
commit
06a644eccd
@ -20,6 +20,7 @@ import android.os.ConditionVariable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||
@ -329,7 +330,12 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
|
||||
// Check for a valid response code.
|
||||
int responseCode = info.getHttpStatusCode();
|
||||
if (responseCode < 200 || responseCode > 299) {
|
||||
throw new InvalidResponseCodeException(responseCode, info.getAllHeaders(), currentDataSpec);
|
||||
InvalidResponseCodeException exception = new InvalidResponseCodeException(
|
||||
responseCode, info.getAllHeaders(), currentDataSpec);
|
||||
if (responseCode == 416) {
|
||||
exception.initCause(new DataSourceException(DataSourceException.POSITION_OUT_OF_RANGE));
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
// Check for a valid content type.
|
||||
try {
|
||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.okhttp;
|
||||
|
||||
import android.net.Uri;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||
@ -162,7 +163,12 @@ public class OkHttpDataSource implements HttpDataSource {
|
||||
if (!response.isSuccessful()) {
|
||||
Map<String, List<String>> headers = request.headers().toMultimap();
|
||||
closeConnectionQuietly();
|
||||
throw new InvalidResponseCodeException(responseCode, headers, dataSpec);
|
||||
InvalidResponseCodeException exception = new InvalidResponseCodeException(
|
||||
responseCode, headers, dataSpec);
|
||||
if (responseCode == 416) {
|
||||
exception.initCause(new DataSourceException(DataSourceException.POSITION_OUT_OF_RANGE));
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
|
||||
// Check for a valid content type.
|
||||
|
@ -23,7 +23,7 @@ import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSource.Builder;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
@ -55,7 +55,7 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
public void testMaxCacheFileSize() throws Exception {
|
||||
CacheDataSource cacheDataSource = createCacheDataSource(false, false, false);
|
||||
CacheDataSource cacheDataSource = createCacheDataSource(false, false);
|
||||
assertReadDataContentLength(cacheDataSource, false, false);
|
||||
assertEquals((int) Math.ceil((double) TEST_DATA.length / MAX_CACHE_FILE_SIZE),
|
||||
cacheDir.listFiles().length);
|
||||
@ -85,28 +85,28 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
||||
|
||||
// Now do an unbounded request. This will read all of the data from cache and then try to read
|
||||
// more from upstream which will cause to a 416 so CDS will store the length.
|
||||
CacheDataSource cacheDataSource = createCacheDataSource(true, true, true);
|
||||
CacheDataSource cacheDataSource = createCacheDataSource(true, true);
|
||||
assertReadDataContentLength(cacheDataSource, true, true);
|
||||
|
||||
// If the user try to access off range then it should throw an IOException
|
||||
try {
|
||||
cacheDataSource = createCacheDataSource(false, false, false);
|
||||
cacheDataSource = createCacheDataSource(false, false);
|
||||
cacheDataSource.open(new DataSpec(Uri.EMPTY, TEST_DATA.length, 5, KEY_1));
|
||||
fail();
|
||||
} catch (TestIOException e) {
|
||||
} catch (IOException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
public void testContentLengthEdgeCases() throws Exception {
|
||||
// Read partial at EOS but don't cross it so length is unknown
|
||||
CacheDataSource cacheDataSource = createCacheDataSource(false, false, true);
|
||||
CacheDataSource cacheDataSource = createCacheDataSource(false, true);
|
||||
assertReadData(cacheDataSource, true, TEST_DATA.length - 2, 2);
|
||||
assertEquals(C.LENGTH_UNSET, simpleCache.getContentLength(KEY_1));
|
||||
|
||||
// Now do an unbounded request for whole data. This will cause a bounded request from upstream.
|
||||
// End of data from upstream shouldn't be mixed up with EOS and cause length set wrong.
|
||||
cacheDataSource = createCacheDataSource(true, false, true);
|
||||
cacheDataSource = createCacheDataSource(false, true);
|
||||
assertReadDataContentLength(cacheDataSource, true, true);
|
||||
|
||||
// Now the length set correctly do an unbounded request with offset
|
||||
@ -121,11 +121,11 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
||||
private void assertCacheAndRead(boolean unboundedRequest, boolean simulateUnknownLength)
|
||||
throws IOException {
|
||||
// Read all data from upstream and cache
|
||||
CacheDataSource cacheDataSource = createCacheDataSource(false, false, simulateUnknownLength);
|
||||
CacheDataSource cacheDataSource = createCacheDataSource(false, simulateUnknownLength);
|
||||
assertReadDataContentLength(cacheDataSource, unboundedRequest, simulateUnknownLength);
|
||||
|
||||
// Just read from cache
|
||||
cacheDataSource = createCacheDataSource(false, true, simulateUnknownLength);
|
||||
cacheDataSource = createCacheDataSource(true, simulateUnknownLength);
|
||||
assertReadDataContentLength(cacheDataSource, unboundedRequest,
|
||||
false /*length is already cached*/);
|
||||
}
|
||||
@ -168,7 +168,7 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
||||
cacheDataSource.close();
|
||||
}
|
||||
|
||||
private CacheDataSource createCacheDataSource(boolean set416exception, boolean setReadException,
|
||||
private CacheDataSource createCacheDataSource(boolean setReadException,
|
||||
boolean simulateUnknownLength) {
|
||||
Builder builder = new Builder();
|
||||
if (setReadException) {
|
||||
@ -177,14 +177,9 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
||||
builder.setSimulateUnknownLength(simulateUnknownLength);
|
||||
builder.appendReadData(TEST_DATA);
|
||||
FakeDataSource upstream = builder.build();
|
||||
upstream.setUnsatisfiableRangeException(set416exception
|
||||
? new InvalidResponseCodeException(416, null, null)
|
||||
: new TestIOException());
|
||||
return new CacheDataSource(simpleCache, upstream,
|
||||
CacheDataSource.FLAG_BLOCK_ON_CACHE | CacheDataSource.FLAG_CACHE_UNBOUNDED_REQUESTS,
|
||||
MAX_CACHE_FILE_SIZE);
|
||||
}
|
||||
|
||||
private static class TestIOException extends IOException {}
|
||||
|
||||
}
|
||||
|
@ -43,7 +43,8 @@ public interface DataSource {
|
||||
* that any partial effects of the invocation are cleaned up.
|
||||
*
|
||||
* @param dataSpec Defines the data to be read.
|
||||
* @throws IOException If an error occurs opening the source.
|
||||
* @throws IOException If an error occurs opening the source. {@link DataSourceException} can be
|
||||
* thrown or used as a cause of the thrown exception to specify the reason of the error.
|
||||
* @return The number of bytes that can be read from the opened source. For unbounded requests
|
||||
* (i.e. requests where {@link DataSpec#length} equals {@link C#LENGTH_UNSET}) this value
|
||||
* is the resolved length of the request, or {@link C#LENGTH_UNSET} if the length is still
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Used to specify reason of a DataSource error.
|
||||
*/
|
||||
public final class DataSourceException extends IOException {
|
||||
|
||||
public static final int POSITION_OUT_OF_RANGE = 0;
|
||||
|
||||
/**
|
||||
* The reason of this {@link DataSourceException}. It can only be {@link #POSITION_OUT_OF_RANGE}.
|
||||
*/
|
||||
public final int reason;
|
||||
|
||||
/**
|
||||
* Constructs a DataSourceException.
|
||||
*
|
||||
* @param reason Reason of the error. It can only be {@link #POSITION_OUT_OF_RANGE}.
|
||||
*/
|
||||
public DataSourceException(int reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
}
|
@ -209,7 +209,12 @@ public class DefaultHttpDataSource implements HttpDataSource {
|
||||
if (responseCode < 200 || responseCode > 299) {
|
||||
Map<String, List<String>> headers = connection.getHeaderFields();
|
||||
closeConnectionQuietly();
|
||||
throw new InvalidResponseCodeException(responseCode, headers, dataSpec);
|
||||
InvalidResponseCodeException exception =
|
||||
new InvalidResponseCodeException(responseCode, headers, dataSpec);
|
||||
if (responseCode == 416) {
|
||||
exception.initCause(new DataSourceException(DataSourceException.POSITION_OUT_OF_RANGE));
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
|
||||
// Check for a valid content type.
|
||||
|
@ -20,9 +20,9 @@ import android.util.Log;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.DataSink;
|
||||
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.FileDataSource;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
|
||||
import com.google.android.exoplayer2.upstream.TeeDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSink.CacheDataSinkException;
|
||||
import java.io.IOException;
|
||||
@ -184,6 +184,9 @@ public final class CacheDataSource implements DataSource {
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer, int offset, int max) throws IOException {
|
||||
if (bytesRemaining == 0) {
|
||||
return C.RESULT_END_OF_INPUT;
|
||||
}
|
||||
try {
|
||||
int bytesRead = currentDataSource.read(buffer, offset, max);
|
||||
if (bytesRead >= 0) {
|
||||
@ -287,23 +290,34 @@ public final class CacheDataSource implements DataSource {
|
||||
|
||||
currentRequestUnbounded = dataSpec.length == C.LENGTH_UNSET;
|
||||
boolean successful = false;
|
||||
long currentBytesRemaining;
|
||||
long currentBytesRemaining = 0;
|
||||
try {
|
||||
currentBytesRemaining = currentDataSource.open(dataSpec);
|
||||
successful = true;
|
||||
} catch (InvalidResponseCodeException e) {
|
||||
// if this isn't the initial open call (we had read some bytes) and got an 'unsatisfiable
|
||||
// byte-range' (416) response for an unbounded range request then mute the exception. We are
|
||||
// trying to find the stream end.
|
||||
if (!initial && e.responseCode == 416 && currentRequestUnbounded) {
|
||||
currentBytesRemaining = 0;
|
||||
} else {
|
||||
} catch (IOException e) {
|
||||
// if this isn't the initial open call (we had read some bytes) and an unbounded range request
|
||||
// failed because of POSITION_OUT_OF_RANGE then mute the exception. We are trying to find the
|
||||
// end of the stream.
|
||||
if (!initial && currentRequestUnbounded) {
|
||||
Throwable cause = e;
|
||||
while (cause != null) {
|
||||
if (cause instanceof DataSourceException) {
|
||||
int reason = ((DataSourceException) cause).reason;
|
||||
if (reason == DataSourceException.POSITION_OUT_OF_RANGE) {
|
||||
e = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cause = cause.getCause();
|
||||
}
|
||||
}
|
||||
if (e != null) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// If we did an unbounded request (which means bytesRemaining == C.LENGTH_UNSET) and got a
|
||||
// resolved length from open() request
|
||||
// If we did an unbounded request (which means it's to upstream and
|
||||
// bytesRemaining == C.LENGTH_UNSET) and got a resolved length from open() request
|
||||
if (currentRequestUnbounded && currentBytesRemaining != C.LENGTH_UNSET) {
|
||||
bytesRemaining = currentBytesRemaining;
|
||||
// If writing into cache
|
||||
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.testutil;
|
||||
import android.net.Uri;
|
||||
import com.google.android.exoplayer2.C;
|
||||
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.util.Assertions;
|
||||
import java.io.IOException;
|
||||
@ -49,7 +50,6 @@ public final class FakeDataSource implements DataSource {
|
||||
private boolean opened;
|
||||
private int currentSegmentIndex;
|
||||
private long bytesRemaining;
|
||||
private IOException unsatisfiableRangeException;
|
||||
|
||||
private FakeDataSource(boolean simulateUnknownLength, ArrayList<Segment> segments) {
|
||||
this.simulateUnknownLength = simulateUnknownLength;
|
||||
@ -60,7 +60,6 @@ public final class FakeDataSource implements DataSource {
|
||||
}
|
||||
this.totalLength = totalLength;
|
||||
openedDataSpecs = new ArrayList<>();
|
||||
unsatisfiableRangeException = new IOException("Unsatisfiable range");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,7 +72,7 @@ public final class FakeDataSource implements DataSource {
|
||||
// If the source knows that the request is unsatisfiable then fail.
|
||||
if (dataSpec.position >= totalLength || (dataSpec.length != C.LENGTH_UNSET
|
||||
&& (dataSpec.position + dataSpec.length > totalLength))) {
|
||||
throw (IOException) unsatisfiableRangeException.fillInStackTrace();
|
||||
throw new DataSourceException(DataSourceException.POSITION_OUT_OF_RANGE);
|
||||
}
|
||||
// Scan through the segments, configuring them for the current read.
|
||||
boolean findingCurrentSegmentIndex = true;
|
||||
@ -160,10 +159,6 @@ public final class FakeDataSource implements DataSource {
|
||||
return dataSpecs;
|
||||
}
|
||||
|
||||
public void setUnsatisfiableRangeException(IOException unsatisfiableRangeException) {
|
||||
this.unsatisfiableRangeException = unsatisfiableRangeException;
|
||||
}
|
||||
|
||||
private static class Segment {
|
||||
|
||||
public final IOException exception;
|
||||
|
Loading…
x
Reference in New Issue
Block a user