diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java index 30b926348dac09c8d6d7974d766d2d7fc97e4993..c0927eb7db2566f87ef1ac5dc0ee474d72daec64 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java @@ -26,6 +26,7 @@ import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.FileDataSource; import com.google.android.exoplayer2.upstream.TeeDataSource; import com.google.android.exoplayer2.upstream.cache.Cache.CacheException; +import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; import java.io.InterruptedIOException; import java.lang.annotation.Retention; @@ -280,8 +281,14 @@ public final class CacheDataSource implements DataSource { * {@link #cacheReadDataSource} is opened to read from it. Else {@link #upstreamDataSource} is * opened to read from the upstream source and write into the cache. * - * @param checkCache If true tries to switch reading from or writing to cache instead of reading - * from upstream. If the switch isn't possible then returns without changing source. + *

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 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(boolean checkCache) throws IOException { CacheSpan nextSpan; @@ -335,22 +342,33 @@ public final class CacheDataSource implements DataSource { } } - if (!currentRequestIgnoresCache && nextDataSource == upstreamDataSource) { - checkCachePosition = readPosition + MIN_READ_BEFORE_CHECKING_CACHE; - if (checkCache) { + checkCachePosition = + !currentRequestIgnoresCache && nextDataSource == upstreamDataSource + ? readPosition + MIN_READ_BEFORE_CHECKING_CACHE + : Long.MAX_VALUE; + if (checkCache) { + Assertions.checkState(currentDataSource == upstreamDataSource); + if (nextDataSource == upstreamDataSource) { + // Continue reading from upstream. return; } - } else { - checkCachePosition = Long.MAX_VALUE; + // We're switching to reading from or writing to the cache. + try { + closeCurrentSource(); + } catch (Throwable e) { + if (nextSpan.isHoleSpan()) { + // Release the hole span before throwing, else we'll hold it forever. + cache.releaseHoleSpan(nextSpan); + } + throw e; + } } - closeCurrentSource(); if (nextSpan != null && nextSpan.isHoleSpan()) { currentHoleSpan = nextSpan; } currentDataSource = nextDataSource; currentDataSpecLengthUnset = nextDataSpec.length == C.LENGTH_UNSET; - long resolvedLength = nextDataSource.open(nextDataSpec); if (currentDataSpecLengthUnset && resolvedLength != C.LENGTH_UNSET) { setBytesRemaining(resolvedLength); @@ -388,9 +406,9 @@ public final class CacheDataSource implements DataSource { } try { currentDataSource.close(); + } finally { currentDataSource = null; currentDataSpecLengthUnset = false; - } finally { if (currentHoleSpan != null) { cache.releaseHoleSpan(currentHoleSpan); currentHoleSpan = null;