Search in sources :

Example 21 with Cache

use of com.google.android.exoplayer2.upstream.cache.Cache 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 22 with Cache

use of com.google.android.exoplayer2.upstream.cache.Cache 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)

Example 23 with Cache

use of com.google.android.exoplayer2.upstream.cache.Cache in project ExoPlayer by google.

the class HlsDownloaderTest method setUp.

@Before
public void setUp() throws Exception {
    tempFolder = Util.createTempDirectory(ApplicationProvider.getApplicationContext(), "ExoPlayerTest");
    cache = new SimpleCache(tempFolder, new NoOpCacheEvictor(), TestUtil.getInMemoryDatabaseProvider());
    progressListener = new ProgressListener();
    fakeDataSet = new FakeDataSet().setData(MULTIVARIANT_PLAYLIST_URI, MULTIVARIANT_PLAYLIST_DATA).setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA).setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10).setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11).setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence2.ts", 12).setData(MEDIA_PLAYLIST_2_URI, MEDIA_PLAYLIST_DATA).setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence0.ts", 13).setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence1.ts", 14).setRandomData(MEDIA_PLAYLIST_2_DIR + "fileSequence2.ts", 15);
}
Also used : SimpleCache(com.google.android.exoplayer2.upstream.cache.SimpleCache) NoOpCacheEvictor(com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor) FakeDataSet(com.google.android.exoplayer2.testutil.FakeDataSet) Before(org.junit.Before)

Example 24 with Cache

use of com.google.android.exoplayer2.upstream.cache.Cache in project ExoPlayer by google.

the class CacheAsserts method assertDataCached.

/**
 * Asserts that the cache contains the given data for {@code dataSpec}.
 *
 * @throws IOException If an error occurred reading from the Cache.
 */
public static void assertDataCached(Cache cache, DataSpec dataSpec, byte[] expected) throws IOException {
    DataSource dataSource = new CacheDataSource(cache, DummyDataSource.INSTANCE, 0);
    byte[] bytes;
    try {
        dataSource.open(dataSpec);
        bytes = DataSourceUtil.readToEnd(dataSource);
    } catch (IOException e) {
        throw new IOException("Opening/reading cache failed: " + dataSpec, e);
    } finally {
        dataSource.close();
    }
    assertWithMessage("Cached data doesn't match expected for '" + dataSpec.uri + "',").that(bytes).isEqualTo(expected);
}
Also used : CacheDataSource(com.google.android.exoplayer2.upstream.cache.CacheDataSource) IOException(java.io.IOException) CacheDataSource(com.google.android.exoplayer2.upstream.cache.CacheDataSource) DataSource(com.google.android.exoplayer2.upstream.DataSource) DummyDataSource(com.google.android.exoplayer2.upstream.DummyDataSource)

Example 25 with Cache

use of com.google.android.exoplayer2.upstream.cache.Cache in project ExoPlayer by google.

the class CacheAsserts method assertReadData.

/**
 * Asserts that the read data from {@code dataSource} specified by {@code dataSpec} is equal to
 * {@code expected} or not.
 *
 * @throws IOException If an error occurred reading from the Cache.
 */
public static void assertReadData(DataSource dataSource, DataSpec dataSpec, byte[] expected) throws IOException {
    try (DataSourceInputStream inputStream = new DataSourceInputStream(dataSource, dataSpec)) {
        byte[] bytes = Util.toByteArray(inputStream);
        assertThat(bytes).isEqualTo(expected);
    }
}
Also used : DataSourceInputStream(com.google.android.exoplayer2.upstream.DataSourceInputStream)

Aggregations

Test (org.junit.Test)25 FakeDataSource (com.google.android.exoplayer2.testutil.FakeDataSource)19 FakeDataSet (com.google.android.exoplayer2.testutil.FakeDataSet)18 DataSpec (com.google.android.exoplayer2.upstream.DataSpec)16 SimpleCache (com.google.android.exoplayer2.upstream.cache.SimpleCache)10 StreamKey (com.google.android.exoplayer2.offline.StreamKey)9 File (java.io.File)9 DataSource (com.google.android.exoplayer2.upstream.DataSource)8 CacheDataSource (com.google.android.exoplayer2.upstream.cache.CacheDataSource)8 Uri (android.net.Uri)7 FileDataSource (com.google.android.exoplayer2.upstream.FileDataSource)6 NoOpCacheEvictor (com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor)6 IOException (java.io.IOException)6 Before (org.junit.Before)6 RequestSet (com.google.android.exoplayer2.testutil.CacheAsserts.RequestSet)5 Nullable (androidx.annotation.Nullable)3 LeastRecentlyUsedCacheEvictor (com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor)3 Notification (android.app.Notification)2 NotificationManager (android.app.NotificationManager)2 Context (android.content.Context)2