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.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
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.
|
// Check for a valid response code.
|
||||||
int responseCode = info.getHttpStatusCode();
|
int responseCode = info.getHttpStatusCode();
|
||||||
if (responseCode < 200 || responseCode > 299) {
|
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.
|
// Check for a valid content type.
|
||||||
try {
|
try {
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.okhttp;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import com.google.android.exoplayer2.C;
|
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.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
@ -162,7 +163,12 @@ public class OkHttpDataSource implements HttpDataSource {
|
|||||||
if (!response.isSuccessful()) {
|
if (!response.isSuccessful()) {
|
||||||
Map<String, List<String>> headers = request.headers().toMultimap();
|
Map<String, List<String>> headers = request.headers().toMultimap();
|
||||||
closeConnectionQuietly();
|
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.
|
// 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.FakeDataSource.Builder;
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -55,7 +55,7 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testMaxCacheFileSize() throws Exception {
|
public void testMaxCacheFileSize() throws Exception {
|
||||||
CacheDataSource cacheDataSource = createCacheDataSource(false, false, false);
|
CacheDataSource cacheDataSource = createCacheDataSource(false, false);
|
||||||
assertReadDataContentLength(cacheDataSource, false, false);
|
assertReadDataContentLength(cacheDataSource, false, false);
|
||||||
assertEquals((int) Math.ceil((double) TEST_DATA.length / MAX_CACHE_FILE_SIZE),
|
assertEquals((int) Math.ceil((double) TEST_DATA.length / MAX_CACHE_FILE_SIZE),
|
||||||
cacheDir.listFiles().length);
|
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
|
// 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.
|
// 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);
|
assertReadDataContentLength(cacheDataSource, true, true);
|
||||||
|
|
||||||
// If the user try to access off range then it should throw an IOException
|
// If the user try to access off range then it should throw an IOException
|
||||||
try {
|
try {
|
||||||
cacheDataSource = createCacheDataSource(false, false, false);
|
cacheDataSource = createCacheDataSource(false, false);
|
||||||
cacheDataSource.open(new DataSpec(Uri.EMPTY, TEST_DATA.length, 5, KEY_1));
|
cacheDataSource.open(new DataSpec(Uri.EMPTY, TEST_DATA.length, 5, KEY_1));
|
||||||
fail();
|
fail();
|
||||||
} catch (TestIOException e) {
|
} catch (IOException e) {
|
||||||
// success
|
// success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testContentLengthEdgeCases() throws Exception {
|
public void testContentLengthEdgeCases() throws Exception {
|
||||||
// Read partial at EOS but don't cross it so length is unknown
|
// 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);
|
assertReadData(cacheDataSource, true, TEST_DATA.length - 2, 2);
|
||||||
assertEquals(C.LENGTH_UNSET, simpleCache.getContentLength(KEY_1));
|
assertEquals(C.LENGTH_UNSET, simpleCache.getContentLength(KEY_1));
|
||||||
|
|
||||||
// Now do an unbounded request for whole data. This will cause a bounded request from upstream.
|
// 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.
|
// 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);
|
assertReadDataContentLength(cacheDataSource, true, true);
|
||||||
|
|
||||||
// Now the length set correctly do an unbounded request with offset
|
// 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)
|
private void assertCacheAndRead(boolean unboundedRequest, boolean simulateUnknownLength)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
// Read all data from upstream and cache
|
// Read all data from upstream and cache
|
||||||
CacheDataSource cacheDataSource = createCacheDataSource(false, false, simulateUnknownLength);
|
CacheDataSource cacheDataSource = createCacheDataSource(false, simulateUnknownLength);
|
||||||
assertReadDataContentLength(cacheDataSource, unboundedRequest, simulateUnknownLength);
|
assertReadDataContentLength(cacheDataSource, unboundedRequest, simulateUnknownLength);
|
||||||
|
|
||||||
// Just read from cache
|
// Just read from cache
|
||||||
cacheDataSource = createCacheDataSource(false, true, simulateUnknownLength);
|
cacheDataSource = createCacheDataSource(true, simulateUnknownLength);
|
||||||
assertReadDataContentLength(cacheDataSource, unboundedRequest,
|
assertReadDataContentLength(cacheDataSource, unboundedRequest,
|
||||||
false /*length is already cached*/);
|
false /*length is already cached*/);
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
|||||||
cacheDataSource.close();
|
cacheDataSource.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CacheDataSource createCacheDataSource(boolean set416exception, boolean setReadException,
|
private CacheDataSource createCacheDataSource(boolean setReadException,
|
||||||
boolean simulateUnknownLength) {
|
boolean simulateUnknownLength) {
|
||||||
Builder builder = new Builder();
|
Builder builder = new Builder();
|
||||||
if (setReadException) {
|
if (setReadException) {
|
||||||
@ -177,14 +177,9 @@ public class CacheDataSourceTest extends InstrumentationTestCase {
|
|||||||
builder.setSimulateUnknownLength(simulateUnknownLength);
|
builder.setSimulateUnknownLength(simulateUnknownLength);
|
||||||
builder.appendReadData(TEST_DATA);
|
builder.appendReadData(TEST_DATA);
|
||||||
FakeDataSource upstream = builder.build();
|
FakeDataSource upstream = builder.build();
|
||||||
upstream.setUnsatisfiableRangeException(set416exception
|
|
||||||
? new InvalidResponseCodeException(416, null, null)
|
|
||||||
: new TestIOException());
|
|
||||||
return new CacheDataSource(simpleCache, upstream,
|
return new CacheDataSource(simpleCache, upstream,
|
||||||
CacheDataSource.FLAG_BLOCK_ON_CACHE | CacheDataSource.FLAG_CACHE_UNBOUNDED_REQUESTS,
|
CacheDataSource.FLAG_BLOCK_ON_CACHE | CacheDataSource.FLAG_CACHE_UNBOUNDED_REQUESTS,
|
||||||
MAX_CACHE_FILE_SIZE);
|
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.
|
* that any partial effects of the invocation are cleaned up.
|
||||||
*
|
*
|
||||||
* @param dataSpec Defines the data to be read.
|
* @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
|
* @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
|
* (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
|
* 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) {
|
if (responseCode < 200 || responseCode > 299) {
|
||||||
Map<String, List<String>> headers = connection.getHeaderFields();
|
Map<String, List<String>> headers = connection.getHeaderFields();
|
||||||
closeConnectionQuietly();
|
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.
|
// 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.C;
|
||||||
import com.google.android.exoplayer2.upstream.DataSink;
|
import com.google.android.exoplayer2.upstream.DataSink;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.FileDataSource;
|
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.TeeDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSink.CacheDataSinkException;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSink.CacheDataSinkException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -184,6 +184,9 @@ public final class CacheDataSource implements DataSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer, int offset, int max) throws IOException {
|
public int read(byte[] buffer, int offset, int max) throws IOException {
|
||||||
|
if (bytesRemaining == 0) {
|
||||||
|
return C.RESULT_END_OF_INPUT;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
int bytesRead = currentDataSource.read(buffer, offset, max);
|
int bytesRead = currentDataSource.read(buffer, offset, max);
|
||||||
if (bytesRead >= 0) {
|
if (bytesRead >= 0) {
|
||||||
@ -287,23 +290,34 @@ public final class CacheDataSource implements DataSource {
|
|||||||
|
|
||||||
currentRequestUnbounded = dataSpec.length == C.LENGTH_UNSET;
|
currentRequestUnbounded = dataSpec.length == C.LENGTH_UNSET;
|
||||||
boolean successful = false;
|
boolean successful = false;
|
||||||
long currentBytesRemaining;
|
long currentBytesRemaining = 0;
|
||||||
try {
|
try {
|
||||||
currentBytesRemaining = currentDataSource.open(dataSpec);
|
currentBytesRemaining = currentDataSource.open(dataSpec);
|
||||||
successful = true;
|
successful = true;
|
||||||
} catch (InvalidResponseCodeException e) {
|
} catch (IOException e) {
|
||||||
// if this isn't the initial open call (we had read some bytes) and got an 'unsatisfiable
|
// if this isn't the initial open call (we had read some bytes) and an unbounded range request
|
||||||
// byte-range' (416) response for an unbounded range request then mute the exception. We are
|
// failed because of POSITION_OUT_OF_RANGE then mute the exception. We are trying to find the
|
||||||
// trying to find the stream end.
|
// end of the stream.
|
||||||
if (!initial && e.responseCode == 416 && currentRequestUnbounded) {
|
if (!initial && currentRequestUnbounded) {
|
||||||
currentBytesRemaining = 0;
|
Throwable cause = e;
|
||||||
} else {
|
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;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we did an unbounded request (which means bytesRemaining == C.LENGTH_UNSET) and got a
|
// If we did an unbounded request (which means it's to upstream and
|
||||||
// resolved length from open() request
|
// bytesRemaining == C.LENGTH_UNSET) and got a resolved length from open() request
|
||||||
if (currentRequestUnbounded && currentBytesRemaining != C.LENGTH_UNSET) {
|
if (currentRequestUnbounded && currentBytesRemaining != C.LENGTH_UNSET) {
|
||||||
bytesRemaining = currentBytesRemaining;
|
bytesRemaining = currentBytesRemaining;
|
||||||
// If writing into cache
|
// If writing into cache
|
||||||
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.testutil;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -49,7 +50,6 @@ public final class FakeDataSource implements DataSource {
|
|||||||
private boolean opened;
|
private boolean opened;
|
||||||
private int currentSegmentIndex;
|
private int currentSegmentIndex;
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
private IOException unsatisfiableRangeException;
|
|
||||||
|
|
||||||
private FakeDataSource(boolean simulateUnknownLength, ArrayList<Segment> segments) {
|
private FakeDataSource(boolean simulateUnknownLength, ArrayList<Segment> segments) {
|
||||||
this.simulateUnknownLength = simulateUnknownLength;
|
this.simulateUnknownLength = simulateUnknownLength;
|
||||||
@ -60,7 +60,6 @@ public final class FakeDataSource implements DataSource {
|
|||||||
}
|
}
|
||||||
this.totalLength = totalLength;
|
this.totalLength = totalLength;
|
||||||
openedDataSpecs = new ArrayList<>();
|
openedDataSpecs = new ArrayList<>();
|
||||||
unsatisfiableRangeException = new IOException("Unsatisfiable range");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -73,7 +72,7 @@ public final class FakeDataSource implements DataSource {
|
|||||||
// If the source knows that the request is unsatisfiable then fail.
|
// If the source knows that the request is unsatisfiable then fail.
|
||||||
if (dataSpec.position >= totalLength || (dataSpec.length != C.LENGTH_UNSET
|
if (dataSpec.position >= totalLength || (dataSpec.length != C.LENGTH_UNSET
|
||||||
&& (dataSpec.position + dataSpec.length > totalLength))) {
|
&& (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.
|
// Scan through the segments, configuring them for the current read.
|
||||||
boolean findingCurrentSegmentIndex = true;
|
boolean findingCurrentSegmentIndex = true;
|
||||||
@ -160,10 +159,6 @@ public final class FakeDataSource implements DataSource {
|
|||||||
return dataSpecs;
|
return dataSpecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnsatisfiableRangeException(IOException unsatisfiableRangeException) {
|
|
||||||
this.unsatisfiableRangeException = unsatisfiableRangeException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Segment {
|
private static class Segment {
|
||||||
|
|
||||||
public final IOException exception;
|
public final IOException exception;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user