Search in sources :

Example 1 with DataSpec

use of androidx.media3.datasource.DataSpec in project media by androidx.

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);
}
Also used : HttpURLConnection(java.net.HttpURLConnection) NoRouteToHostException(java.net.NoRouteToHostException) URL(java.net.URL) HttpMethod(androidx.media3.datasource.DataSpec.HttpMethod) Nullable(androidx.annotation.Nullable)

Example 2 with DataSpec

use of androidx.media3.datasource.DataSpec in project media by androidx.

the class CronetDataSourceTest method allowDirectExecutor.

@Test
public void allowDirectExecutor() throws HttpDataSourceException {
    testDataSpec = new DataSpec(Uri.parse(TEST_URL));
    mockResponseStartSuccess();
    dataSourceUnderTest.open(testDataSpec);
    verify(mockUrlRequestBuilder).allowDirectExecutor();
}
Also used : DataSpec(androidx.media3.datasource.DataSpec) Test(org.junit.Test)

Example 3 with DataSpec

use of androidx.media3.datasource.DataSpec in project media by androidx.

the class CronetDataSourceTest method requestOpenGzippedCompressedReturnsDataSpecLength.

@Test
public void requestOpenGzippedCompressedReturnsDataSpecLength() throws HttpDataSourceException {
    testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, 5000);
    testResponseHeader.put("Content-Encoding", "gzip");
    testResponseHeader.put("Content-Length", Long.toString(50L));
    mockResponseStartSuccess();
    assertThat(dataSourceUnderTest.open(testDataSpec)).isEqualTo(5000);
    verify(mockTransferListener).onTransferStart(dataSourceUnderTest, testDataSpec, /* isNetwork= */
    true);
}
Also used : DataSpec(androidx.media3.datasource.DataSpec) Test(org.junit.Test)

Example 4 with DataSpec

use of androidx.media3.datasource.DataSpec in project media by androidx.

the class CronetDataSourceTest method rangeRequestWith200Response.

@Test
public void rangeRequestWith200Response() throws HttpDataSourceException {
    mockResponseStartSuccess();
    mockReadSuccess(0, 7000);
    // Server does not support range requests.
    testUrlResponseInfo = createUrlResponseInfo(200);
    testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000);
    long length = dataSourceUnderTest.open(testDataSpec);
    assertThat(length).isEqualTo(5000);
    byte[] returnedBuffer = new byte[16];
    int bytesRead = dataSourceUnderTest.read(returnedBuffer, 0, 16);
    assertThat(bytesRead).isEqualTo(16);
    assertThat(returnedBuffer).isEqualTo(buildTestDataArray(1000, 16));
    verify(mockTransferListener).onBytesTransferred(dataSourceUnderTest, testDataSpec, /* isNetwork= */
    true, 16);
}
Also used : DataSpec(androidx.media3.datasource.DataSpec) Test(org.junit.Test)

Example 5 with DataSpec

use of androidx.media3.datasource.DataSpec in project media by androidx.

the class OkHttpDataSource method open.

@Override
public long open(DataSpec dataSpec) throws HttpDataSourceException {
    this.dataSpec = dataSpec;
    bytesRead = 0;
    bytesToRead = 0;
    transferInitializing(dataSpec);
    Request request = makeRequest(dataSpec);
    Response response;
    ResponseBody responseBody;
    try {
        this.response = callFactory.newCall(request).execute();
        response = this.response;
        responseBody = Assertions.checkNotNull(response.body());
        responseByteStream = responseBody.byteStream();
    } catch (IOException e) {
        throw HttpDataSourceException.createForIOException(e, dataSpec, HttpDataSourceException.TYPE_OPEN);
    }
    int responseCode = response.code();
    // Check for a valid response code.
    if (!response.isSuccessful()) {
        if (responseCode == 416) {
            long documentSize = HttpUtil.getDocumentSize(response.headers().get(HttpHeaders.CONTENT_RANGE));
            if (dataSpec.position == documentSize) {
                opened = true;
                transferStarted(dataSpec);
                return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
            }
        }
        byte[] errorResponseBody;
        try {
            errorResponseBody = Util.toByteArray(Assertions.checkNotNull(responseByteStream));
        } catch (IOException e) {
            errorResponseBody = Util.EMPTY_BYTE_ARRAY;
        }
        Map<String, List<String>> headers = response.headers().toMultimap();
        closeConnectionQuietly();
        @Nullable IOException cause = responseCode == 416 ? new DataSourceException(PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE) : null;
        throw new InvalidResponseCodeException(responseCode, response.message(), cause, headers, dataSpec, errorResponseBody);
    }
    // Check for a valid content type.
    @Nullable MediaType mediaType = responseBody.contentType();
    String contentType = mediaType != null ? mediaType.toString() : "";
    if (contentTypePredicate != null && !contentTypePredicate.apply(contentType)) {
        closeConnectionQuietly();
        throw new InvalidContentTypeException(contentType, dataSpec);
    }
    // If we requested a range starting from a non-zero position and received a 200 rather than a
    // 206, then the server does not support partial requests. We'll need to manually skip to the
    // requested position.
    long bytesToSkip = responseCode == 200 && dataSpec.position != 0 ? dataSpec.position : 0;
    // Determine the length of the data to be read, after skipping.
    if (dataSpec.length != C.LENGTH_UNSET) {
        bytesToRead = dataSpec.length;
    } else {
        long contentLength = responseBody.contentLength();
        bytesToRead = contentLength != -1 ? (contentLength - bytesToSkip) : C.LENGTH_UNSET;
    }
    opened = true;
    transferStarted(dataSpec);
    try {
        skipFully(bytesToSkip, dataSpec);
    } catch (HttpDataSourceException e) {
        closeConnectionQuietly();
        throw e;
    }
    return bytesToRead;
}
Also used : Request(okhttp3.Request) InterruptedIOException(java.io.InterruptedIOException) IOException(java.io.IOException) ResponseBody(okhttp3.ResponseBody) Response(okhttp3.Response) DataSourceException(androidx.media3.datasource.DataSourceException) MediaType(okhttp3.MediaType) List(java.util.List) Nullable(androidx.annotation.Nullable)

Aggregations

DataSpec (androidx.media3.datasource.DataSpec)101 Test (org.junit.Test)71 FakeDataSource (androidx.media3.test.utils.FakeDataSource)27 DataSource (androidx.media3.datasource.DataSource)20 Nullable (androidx.annotation.Nullable)16 IOException (java.io.IOException)14 Uri (android.net.Uri)12 FakeDataSet (androidx.media3.test.utils.FakeDataSet)9 HlsMediaPlaylist (androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist)7 DefaultExtractorInput (androidx.media3.extractor.DefaultExtractorInput)7 ArrayList (java.util.ArrayList)6 DataSourceException (androidx.media3.datasource.DataSourceException)4 DataSourceInputStream (androidx.media3.datasource.DataSourceInputStream)4 HttpDataSource (androidx.media3.datasource.HttpDataSource)4 ExtractorInput (androidx.media3.extractor.ExtractorInput)4 InterruptedIOException (java.io.InterruptedIOException)4 ByteBuffer (java.nio.ByteBuffer)4 HashMap (java.util.HashMap)4 List (java.util.List)4 MockResponse (okhttp3.mockwebserver.MockResponse)4