diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/Cache.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/Cache.java index 76481bbdf727ed083acbd8c8920ed133e72bb311..171aa0878a3fce31fdfb78fd7e80580b6884da3d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/Cache.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/Cache.java @@ -127,23 +127,24 @@ public interface Cache { /** * A caller should invoke this method when they require data from a given position for a given * key. - *

- * If there is a cache entry that overlaps the position, then the returned {@link CacheSpan} + * + *

If there is a cache entry that overlaps the position, then the returned {@link CacheSpan} * defines the file in which the data is stored. {@link CacheSpan#isCached} is true. The caller * may read from the cache file, but does not acquire any locks. - *

- * If there is no cache entry overlapping {@code offset}, then the returned {@link CacheSpan} + * + *

If there is no cache entry overlapping {@code offset}, then the returned {@link CacheSpan} * defines a hole in the cache starting at {@code position} into which the caller may write as it * obtains the data from some other source. The returned {@link CacheSpan} serves as a lock. * Whilst the caller holds the lock it may write data into the hole. It may split data into - * multiple files. When the caller has finished writing a file it should commit it to the cache - * by calling {@link #commitFile(File)}. When the caller has finished writing, it must release - * the lock by calling {@link #releaseHoleSpan}. + * multiple files. When the caller has finished writing a file it should commit it to the cache by + * calling {@link #commitFile(File)}. When the caller has finished writing, it must release the + * lock by calling {@link #releaseHoleSpan}. * * @param key The key of the data being requested. * @param position The position of the data being requested. * @return The {@link CacheSpan}. * @throws InterruptedException If the thread was interrupted. + * @throws CacheException If an error is encountered. */ CacheSpan startReadWrite(String key, long position) throws InterruptedException, CacheException; @@ -154,8 +155,10 @@ public interface Cache { * @param key The key of the data being requested. * @param position The position of the data being requested. * @return The {@link CacheSpan}. Or null if the cache entry is locked. + * @throws CacheException If an error is encountered. */ - @Nullable CacheSpan startReadWriteNonBlocking(String key, long position) throws CacheException; + @Nullable + CacheSpan startReadWriteNonBlocking(String key, long position) throws CacheException; /** * Obtains a cache file into which data can be written. Must only be called when holding a @@ -166,14 +169,16 @@ public interface Cache { * @param maxLength The maximum length of the data to be written. Used only to ensure that there * is enough space in the cache. * @return The file into which data should be written. + * @throws CacheException If an error is encountered. */ File startFile(String key, long position, long maxLength) throws CacheException; /** - * Commits a file into the cache. Must only be called when holding a corresponding hole - * {@link CacheSpan} obtained from {@link #startReadWrite(String, long)} + * Commits a file into the cache. Must only be called when holding a corresponding hole {@link + * CacheSpan} obtained from {@link #startReadWrite(String, long)} * * @param file A newly written cache file. + * @throws CacheException If an error is encountered. */ void commitFile(File file) throws CacheException; @@ -189,6 +194,7 @@ public interface Cache { * Removes a cached {@link CacheSpan} from the cache, deleting the underlying file. * * @param span The {@link CacheSpan} to remove. + * @throws CacheException If an error is encountered. */ void removeSpan(CacheSpan span) throws CacheException; @@ -210,15 +216,16 @@ public interface Cache { * @param key The cache key for the data. * @param position The starting position of the data. * @param length The maximum length of the data to be returned. - * @return the length of the cached or not cached data block length. + * @return The length of the cached or not cached data block length. */ - long getCachedBytes(String key, long position, long length); + long getCachedLength(String key, long position, long length); /** * Sets the content length for the given key. * * @param key The cache key for the data. * @param length The length of the data. + * @throws CacheException If an error is encountered. */ void setContentLength(String key, long length) throws CacheException; @@ -227,7 +234,8 @@ public interface Cache { * com.google.android.exoplayer2.C#LENGTH_UNSET} otherwise. * * @param key The cache key for the data. + * @return The content length for the given key if one set, or {@link + * com.google.android.exoplayer2.C#LENGTH_UNSET} otherwise. */ long getContentLength(String key); - } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java index 1af690e10f2d60f94f76ace0b65db239860f3e42..57f5a6ad9310a5daa9e862c826466ec46ad56251 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java @@ -37,7 +37,7 @@ import java.io.OutputStream; */ public final class CacheDataSink implements DataSink { - /** Default buffer size. */ + /** Default buffer size in bytes. */ public static final int DEFAULT_BUFFER_SIZE = 20480; private final Cache cache; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java index 2bf5cde8e05083c95fce2a2b4e83dc711e62b3f8..22150f8e7865903a971239de33ff74d38a8e96b7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java @@ -89,8 +89,8 @@ public final class CacheUtil { counters.alreadyCachedBytes = 0; counters.newlyCachedBytes = 0; while (left != 0) { - long blockLength = cache.getCachedBytes(key, start, - left != C.LENGTH_UNSET ? left : Long.MAX_VALUE); + long blockLength = + cache.getCachedLength(key, start, left != C.LENGTH_UNSET ? left : Long.MAX_VALUE); if (blockLength > 0) { counters.alreadyCachedBytes += blockLength; } else { @@ -126,6 +126,12 @@ public final class CacheUtil { * Caches the data defined by {@code dataSpec} while skipping already cached data. Caching stops * early if end of input is reached and {@code enableEOFException} is false. * + *

If a {@link PriorityTaskManager} is given, it's used to pause and resume caching depending + * on {@code priority} and the priority of other tasks registered to the PriorityTaskManager. + * Please note that it's the responsibility of the calling code to call {@link + * PriorityTaskManager#add} to register with the manager before calling this method, and to call + * {@link PriorityTaskManager#remove} afterwards to unregister. + * * @param dataSpec Defines the data to be cached. * @param cache A {@link Cache} to store the data. * @param dataSource A {@link CacheDataSource} that works on the {@code cache}. @@ -164,8 +170,8 @@ public final class CacheUtil { long start = dataSpec.absoluteStreamPosition; long left = dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : cache.getContentLength(key); while (left != 0) { - long blockLength = cache.getCachedBytes(key, start, - left != C.LENGTH_UNSET ? left : Long.MAX_VALUE); + long blockLength = + cache.getCachedLength(key, start, left != C.LENGTH_UNSET ? left : Long.MAX_VALUE); if (blockLength > 0) { // Skip already cached data. } else { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContent.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContent.java index fb59d23666e8735309b47a982d99a357b35196a4..34884a457d30a6520d4f676af5eba7d028a10714 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContent.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CachedContent.java @@ -125,7 +125,7 @@ import java.util.TreeSet; * @param length The maximum length of the data to be returned. * @return the length of the cached or not cached data block length. */ - public long getCachedBytes(long position, long length) { + public long getCachedBytesLength(long position, long length) { SimpleCacheSpan span = getSpan(position); if (span.isHoleSpan()) { // We don't have a span covering the start of the queried region. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java index ffac8a35f1874cba8a51094c59b7031d58e34cb7..9d4a66134371ff2a88f9e50a6631e80c4ac0cb1a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java @@ -385,13 +385,13 @@ public final class SimpleCache implements Cache { @Override public synchronized boolean isCached(String key, long position, long length) { CachedContent cachedContent = index.get(key); - return cachedContent != null && cachedContent.getCachedBytes(position, length) >= length; + return cachedContent != null && cachedContent.getCachedBytesLength(position, length) >= length; } @Override - public synchronized long getCachedBytes(String key, long position, long length) { + public synchronized long getCachedLength(String key, long position, long length) { CachedContent cachedContent = index.get(key); - return cachedContent != null ? cachedContent.getCachedBytes(position, length) : -length; + return cachedContent != null ? cachedContent.getCachedBytesLength(position, length) : -length; } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpan.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpan.java index 8c5b7e26e77b3e544cdc8a09289e3a9ca06c7a15..e12d876ce14254bed0ac3bdd23a117667e744da1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpan.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheSpan.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.upstream.cache; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; @@ -35,19 +36,50 @@ import java.util.regex.Pattern; private static final Pattern CACHE_FILE_PATTERN_V3 = Pattern.compile( "^(\\d+)\\.(\\d+)\\.(\\d+)\\.v3\\.exo$", Pattern.DOTALL); - public static File getCacheFile(File cacheDir, int id, long position, - long lastAccessTimestamp) { + /** + * Returns a new {@link File} instance from {@code cacheDir}, {@code id}, {@code position}, {@code + * lastAccessTimestamp}. + * + * @param cacheDir The parent abstract pathname. + * @param id The cache file id. + * @param position The position of the stored data in the original stream. + * @param lastAccessTimestamp The last access timestamp. + * @return The cache file. + */ + public static File getCacheFile(File cacheDir, int id, long position, long lastAccessTimestamp) { return new File(cacheDir, id + "." + position + "." + lastAccessTimestamp + SUFFIX); } + /** + * Creates a lookup span. + * + * @param key The cache key. + * @param position The position of the {@link CacheSpan} in the original stream. + * @return The span. + */ public static SimpleCacheSpan createLookup(String key, long position) { return new SimpleCacheSpan(key, position, C.LENGTH_UNSET, C.TIME_UNSET, null); } + /** + * Creates an open hole span. + * + * @param key The cache key. + * @param position The position of the {@link CacheSpan} in the original stream. + * @return The span. + */ public static SimpleCacheSpan createOpenHole(String key, long position) { return new SimpleCacheSpan(key, position, C.LENGTH_UNSET, C.TIME_UNSET, null); } + /** + * Creates a closed hole span. + * + * @param key The cache key. + * @param position The position of the {@link CacheSpan} in the original stream. + * @param length The length of the {@link CacheSpan}. + * @return The span. + */ public static SimpleCacheSpan createClosedHole(String key, long position, long length) { return new SimpleCacheSpan(key, position, length, C.TIME_UNSET, null); } @@ -60,6 +92,7 @@ import java.util.regex.Pattern; * @return The span, or null if the file name is not correctly formatted, or if the id is not * present in the content index. */ + @Nullable public static SimpleCacheSpan createCacheEntry(File file, CachedContentIndex index) { String name = file.getName(); if (!name.endsWith(SUFFIX)) { @@ -81,6 +114,15 @@ import java.util.regex.Pattern; Long.parseLong(matcher.group(3)), file); } + /** + * Upgrades the cache file if it is created by an earlier version of {@link SimpleCache}. + * + * @param file The cache file. + * @param index Cached content index. + * @return Upgraded cache file or {@code null} if the file name is not correctly formatted or the + * file can not be renamed. + */ + @Nullable private static File upgradeFile(File file, CachedContentIndex index) { String key; String filename = file.getName(); @@ -106,8 +148,17 @@ import java.util.regex.Pattern; return newCacheFile; } - private SimpleCacheSpan(String key, long position, long length, long lastAccessTimestamp, - File file) { + /** + * @param key The cache key. + * @param position The position of the {@link CacheSpan} in the original stream. + * @param length The length of the {@link CacheSpan}, or {@link C#LENGTH_UNSET} if this is an + * open-ended hole. + * @param lastAccessTimestamp The last access timestamp, or {@link C#TIME_UNSET} if {@link + * #isCached} is false. + * @param file The file corresponding to this {@link CacheSpan}, or null if it's a hole. + */ + private SimpleCacheSpan( + String key, long position, long length, long lastAccessTimestamp, @Nullable File file) { super(key, position, length, lastAccessTimestamp, file); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheAsserts.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheAsserts.java index 65850a13e73f0436f7fce83c818e746bcb5e5b01..c31cd0384ee97c59d0a10c2f4167a6aace8196ff 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheAsserts.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheAsserts.java @@ -31,7 +31,11 @@ import java.util.ArrayList; /** Assertion methods for {@link com.google.android.exoplayer2.upstream.cache.Cache}. */ /* package */ final class CacheAsserts { - /** Asserts that the cache content is equal to the data in the {@code fakeDataSet}. */ + /** + * Asserts that the cache content is equal to the data in the {@code fakeDataSet}. + * + * @throws IOException If an error occurred reading from the Cache. + */ public static void assertCachedData(Cache cache, FakeDataSet fakeDataSet) throws IOException { ArrayList allData = fakeDataSet.getAllData(); Uri[] uris = new Uri[allData.size()]; @@ -43,6 +47,8 @@ import java.util.ArrayList; /** * Asserts that the cache content is equal to the given subset of data in the {@code fakeDataSet}. + * + * @throws IOException If an error occurred reading from the Cache. */ public static void assertCachedData(Cache cache, FakeDataSet fakeDataSet, String... uriStrings) throws IOException { @@ -55,6 +61,8 @@ import java.util.ArrayList; /** * Asserts that the cache content is equal to the given subset of data in the {@code fakeDataSet}. + * + * @throws IOException If an error occurred reading from the Cache. */ public static void assertCachedData(Cache cache, FakeDataSet fakeDataSet, Uri... uris) throws IOException { @@ -67,7 +75,11 @@ import java.util.ArrayList; assertThat(cache.getCacheSpace()).isEqualTo(totalLength); } - /** Asserts that the cache contains the given subset of data in the {@code fakeDataSet}. */ + /** + * Asserts that the cache contains the given subset of data in the {@code fakeDataSet}. + * + * @throws IOException If an error occurred reading from the Cache. + */ public static void assertDataCached(Cache cache, FakeDataSet fakeDataSet, Uri... uris) throws IOException { for (Uri uri : uris) { @@ -75,7 +87,11 @@ import java.util.ArrayList; } } - /** Asserts that the cache contains the given data for {@code uriString}. */ + /** + * Asserts that the cache contains the given data for {@code uriString} or not. + * + * @throws IOException If an error occurred reading from the Cache. + */ public static void assertDataCached(Cache cache, Uri uri, byte[] expected) throws IOException { CacheDataSource dataSource = new CacheDataSource(cache, DummyDataSource.INSTANCE, 0); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheUtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheUtilTest.java index c8231ec4ace34cf85288310f8bca36c35c1dce56..250e09bab48b80a60abb119c7122fb92ffe26789 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheUtilTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/CacheUtilTest.java @@ -72,7 +72,7 @@ public final class CacheUtilTest { } @Override - public long getCachedBytes(String key, long position, long length) { + public long getCachedLength(String key, long position, long length) { for (int i = 0; i < spansAndGaps.length; i++) { int spanOrGap = spansAndGaps[i]; if (position < spanOrGap) { diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheTest.java index 75a80185b90d48bd54adfaa079f961980396e93f..e62676fc9d5205694adbc48b4ab9cddf2b038de2 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/cache/SimpleCacheTest.java @@ -218,36 +218,36 @@ public class SimpleCacheTest { } @Test - public void testGetCachedBytes() throws Exception { + public void testGetCachedLength() throws Exception { SimpleCache simpleCache = getSimpleCache(); CacheSpan cacheSpan = simpleCache.startReadWrite(KEY_1, 0); // No cached bytes, returns -'length' - assertThat(simpleCache.getCachedBytes(KEY_1, 0, 100)).isEqualTo(-100); + assertThat(simpleCache.getCachedLength(KEY_1, 0, 100)).isEqualTo(-100); // Position value doesn't affect the return value - assertThat(simpleCache.getCachedBytes(KEY_1, 20, 100)).isEqualTo(-100); + assertThat(simpleCache.getCachedLength(KEY_1, 20, 100)).isEqualTo(-100); addCache(simpleCache, KEY_1, 0, 15); // Returns the length of a single span - assertThat(simpleCache.getCachedBytes(KEY_1, 0, 100)).isEqualTo(15); + assertThat(simpleCache.getCachedLength(KEY_1, 0, 100)).isEqualTo(15); // Value is capped by the 'length' - assertThat(simpleCache.getCachedBytes(KEY_1, 0, 10)).isEqualTo(10); + assertThat(simpleCache.getCachedLength(KEY_1, 0, 10)).isEqualTo(10); addCache(simpleCache, KEY_1, 15, 35); // Returns the length of two adjacent spans - assertThat(simpleCache.getCachedBytes(KEY_1, 0, 100)).isEqualTo(50); + assertThat(simpleCache.getCachedLength(KEY_1, 0, 100)).isEqualTo(50); addCache(simpleCache, KEY_1, 60, 10); // Not adjacent span doesn't affect return value - assertThat(simpleCache.getCachedBytes(KEY_1, 0, 100)).isEqualTo(50); + assertThat(simpleCache.getCachedLength(KEY_1, 0, 100)).isEqualTo(50); // Returns length of hole up to the next cached span - assertThat(simpleCache.getCachedBytes(KEY_1, 55, 100)).isEqualTo(-5); + assertThat(simpleCache.getCachedLength(KEY_1, 55, 100)).isEqualTo(-5); simpleCache.releaseHoleSpan(cacheSpan); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java index eb53191dc883ad0f2c93b68a82b7e3d4d16d1225..2174de1fd596c50bf37f67dee09ac2fadb997875 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java @@ -36,7 +36,11 @@ import java.util.ArrayList; */ public final class CacheAsserts { - /** Asserts that the cache content is equal to the data in the {@code fakeDataSet}. */ + /** + * Asserts that the cache content is equal to the data in the {@code fakeDataSet}. + * + * @throws IOException If an error occurred reading from the Cache. + */ public static void assertCachedData(Cache cache, FakeDataSet fakeDataSet) throws IOException { ArrayList allData = fakeDataSet.getAllData(); Uri[] uris = new Uri[allData.size()]; @@ -48,6 +52,8 @@ public final class CacheAsserts { /** * Asserts that the cache content is equal to the given subset of data in the {@code fakeDataSet}. + * + * @throws IOException If an error occurred reading from the Cache. */ public static void assertCachedData(Cache cache, FakeDataSet fakeDataSet, String... uriStrings) throws IOException { @@ -60,6 +66,8 @@ public final class CacheAsserts { /** * Asserts that the cache content is equal to the given subset of data in the {@code fakeDataSet}. + * + * @throws IOException If an error occurred reading from the Cache. */ public static void assertCachedData(Cache cache, FakeDataSet fakeDataSet, Uri... uris) throws IOException { @@ -72,7 +80,11 @@ public final class CacheAsserts { assertEquals(totalLength, cache.getCacheSpace()); } - /** Asserts that the cache contains the given subset of data in the {@code fakeDataSet}. */ + /** + * Asserts that the cache contains the given subset of data in the {@code fakeDataSet}. + * + * @throws IOException If an error occurred reading from the Cache. + */ public static void assertDataCached(Cache cache, FakeDataSet fakeDataSet, Uri... uris) throws IOException { for (Uri uri : uris) { @@ -80,7 +92,11 @@ public final class CacheAsserts { } } - /** Asserts that the cache contains the given data for {@code uriString}. */ + /** + * Asserts that the cache contains the given data for {@code uriString}. + * + * @throws IOException If an error occurred reading from the Cache. + */ public static void assertDataCached(Cache cache, Uri uri, byte[] expected) throws IOException { CacheDataSource dataSource = new CacheDataSource(cache, DummyDataSource.INSTANCE, 0); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();