Search in sources :

Example 56 with DataSpec

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);
}
Also used : HttpURLConnection(java.net.HttpURLConnection) NoRouteToHostException(java.net.NoRouteToHostException) URL(java.net.URL) HttpMethod(com.google.android.exoplayer2.upstream.DataSpec.HttpMethod) Nullable(androidx.annotation.Nullable)

Example 57 with DataSpec

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");
}
Also used : FakeDataSource(com.google.android.exoplayer2.testutil.FakeDataSource) FakeDataSet(com.google.android.exoplayer2.testutil.FakeDataSet) DataSpec(com.google.android.exoplayer2.upstream.DataSpec) StreamKey(com.google.android.exoplayer2.offline.StreamKey) Test(org.junit.Test)

Example 58 with DataSpec

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");
}
Also used : FakeDataSource(com.google.android.exoplayer2.testutil.FakeDataSource) FakeDataSet(com.google.android.exoplayer2.testutil.FakeDataSet) DataSpec(com.google.android.exoplayer2.upstream.DataSpec) StreamKey(com.google.android.exoplayer2.offline.StreamKey) Test(org.junit.Test)

Example 59 with DataSpec

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);
    }
}
Also used : InterruptedIOException(java.io.InterruptedIOException) DataSpec(com.google.android.exoplayer2.upstream.DataSpec) Uri(android.net.Uri) Nullable(androidx.annotation.Nullable) PriorityDataSource(com.google.android.exoplayer2.upstream.PriorityDataSource) TeeDataSource(com.google.android.exoplayer2.upstream.TeeDataSource) DummyDataSource(com.google.android.exoplayer2.upstream.DummyDataSource) FileDataSource(com.google.android.exoplayer2.upstream.FileDataSource) DataSource(com.google.android.exoplayer2.upstream.DataSource)

Example 60 with DataSpec

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;
    }
}
Also used : DataSourceException(com.google.android.exoplayer2.upstream.DataSourceException) DataSpec(com.google.android.exoplayer2.upstream.DataSpec)

Aggregations

DataSpec (com.google.android.exoplayer2.upstream.DataSpec)118 Test (org.junit.Test)79 FakeDataSource (com.google.android.exoplayer2.testutil.FakeDataSource)28 DataSource (com.google.android.exoplayer2.upstream.DataSource)22 Uri (android.net.Uri)17 Nullable (androidx.annotation.Nullable)17 IOException (java.io.IOException)17 DefaultExtractorInput (com.google.android.exoplayer2.extractor.DefaultExtractorInput)9 FakeDataSet (com.google.android.exoplayer2.testutil.FakeDataSet)9 HlsMediaPlaylist (com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist)8 ArrayList (java.util.ArrayList)7 ExtractorInput (com.google.android.exoplayer2.extractor.ExtractorInput)6 InterruptedIOException (java.io.InterruptedIOException)5 List (java.util.List)5 ContainerMediaChunk (com.google.android.exoplayer2.source.chunk.ContainerMediaChunk)4 Representation (com.google.android.exoplayer2.source.dash.manifest.Representation)4 DataSourceException (com.google.android.exoplayer2.upstream.DataSourceException)4 DataSourceInputStream (com.google.android.exoplayer2.upstream.DataSourceInputStream)4 HttpDataSource (com.google.android.exoplayer2.upstream.HttpDataSource)4 ByteBuffer (java.nio.ByteBuffer)4