Sanitize CacheDataSource/CacheDataSink factories

PiperOrigin-RevId: 307636959
This commit is contained in:
olly 2020-04-21 18:50:27 +01:00 committed by Ian Baker
parent 96ccb893f2
commit fab8087472
6 changed files with 286 additions and 80 deletions

View file

@ -73,6 +73,8 @@
* Fix `AdsMediaSource` child `MediaSource`s not being released.
* Parse track titles from Matroska files
([#7247](https://github.com/google/ExoPlayer/pull/7247)).
* Replace `CacheDataSinkFactory` and `CacheDataSourceFactory` with
`CacheDataSink.Factory` and `CacheDataSource.Factory` respectively.
* Text:
* Parse `<ruby>` and `<rt>` tags in WebVTT subtitles (rendering is coming
later).

View file

@ -19,14 +19,11 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.upstream.DataSink;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.FileDataSource;
import com.google.android.exoplayer2.upstream.PriorityDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.android.exoplayer2.upstream.cache.CacheDataSink;
import com.google.android.exoplayer2.upstream.cache.CacheDataSinkFactory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.CacheKeyFactory;
import com.google.android.exoplayer2.util.PriorityTaskManager;
@ -34,8 +31,8 @@ import com.google.android.exoplayer2.util.PriorityTaskManager;
public final class DownloaderConstructorHelper {
@Nullable private final PriorityTaskManager priorityTaskManager;
private final CacheDataSourceFactory onlineCacheDataSourceFactory;
private final CacheDataSourceFactory offlineCacheDataSourceFactory;
private final CacheDataSource.Factory onlineCacheDataSourceFactory;
private final CacheDataSource.Factory offlineCacheDataSourceFactory;
/**
* @param cache Cache instance to be used to store downloaded data.
@ -100,37 +97,32 @@ public final class DownloaderConstructorHelper {
@Nullable DataSink.Factory cacheWriteDataSinkFactory,
@Nullable PriorityTaskManager priorityTaskManager,
@Nullable CacheKeyFactory cacheKeyFactory) {
this.priorityTaskManager = priorityTaskManager;
if (priorityTaskManager != null) {
upstreamFactory =
new PriorityDataSourceFactory(upstreamFactory, priorityTaskManager, C.PRIORITY_DOWNLOAD);
}
DataSource.Factory readDataSourceFactory =
cacheReadDataSourceFactory != null
? cacheReadDataSourceFactory
: new FileDataSource.Factory();
if (cacheWriteDataSinkFactory == null) {
cacheWriteDataSinkFactory =
new CacheDataSinkFactory(cache, CacheDataSink.DEFAULT_FRAGMENT_SIZE);
}
onlineCacheDataSourceFactory =
new CacheDataSourceFactory(
cache,
upstreamFactory,
readDataSourceFactory,
cacheWriteDataSinkFactory,
CacheDataSource.FLAG_BLOCK_ON_CACHE,
/* eventListener= */ null,
cacheKeyFactory);
new CacheDataSource.Factory()
.setCache(cache)
.setUpstreamDataSourceFactory(upstreamFactory)
.setFlags(CacheDataSource.FLAG_BLOCK_ON_CACHE);
offlineCacheDataSourceFactory =
new CacheDataSourceFactory(
cache,
DummyDataSource.FACTORY,
readDataSourceFactory,
null,
CacheDataSource.FLAG_BLOCK_ON_CACHE,
/* eventListener= */ null,
cacheKeyFactory);
this.priorityTaskManager = priorityTaskManager;
new CacheDataSource.Factory()
.setCache(cache)
.setCacheWriteDataSinkFactory(null)
.setFlags(CacheDataSource.FLAG_BLOCK_ON_CACHE);
if (cacheKeyFactory != null) {
onlineCacheDataSourceFactory.setCacheKeyFactory(cacheKeyFactory);
offlineCacheDataSourceFactory.setCacheKeyFactory(cacheKeyFactory);
}
if (cacheReadDataSourceFactory != null) {
onlineCacheDataSourceFactory.setCacheReadDataSourceFactory(cacheReadDataSourceFactory);
offlineCacheDataSourceFactory.setCacheReadDataSourceFactory(cacheReadDataSourceFactory);
}
if (cacheWriteDataSinkFactory != null) {
onlineCacheDataSourceFactory.setCacheWriteDataSinkFactory(cacheWriteDataSinkFactory);
}
}
/** Returns a {@link PriorityTaskManager} instance. */

View file

@ -39,6 +39,78 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/
public final class CacheDataSink implements DataSink {
/** {@link DataSink.Factory} for {@link CacheDataSink} instances. */
public static final class Factory implements DataSink.Factory {
private @MonotonicNonNull Cache cache;
private long fragmentSize;
private int bufferSize;
/** Creates an instance. */
public Factory() {
fragmentSize = CacheDataSink.DEFAULT_FRAGMENT_SIZE;
bufferSize = CacheDataSink.DEFAULT_BUFFER_SIZE;
}
/**
* Sets the cache to which data will be written.
*
* <p>Must be called before the factory is used.
*
* @param cache The cache to which data will be written.
* @return This factory.
*/
public Factory setCache(Cache cache) {
this.cache = cache;
return this;
}
/**
* Sets the cache file fragment size. For requests that should be fragmented into multiple cache
* files, this is the maximum size of a cache file in bytes. If set to {@link C#LENGTH_UNSET}
* then no fragmentation will occur. Using a small value allows for finer-grained cache eviction
* policies, at the cost of increased overhead both on the cache implementation and the file
* system. Values under {@code (2 * 1024 * 1024)} are not recommended.
*
* <p>The default value is {@link CacheDataSink#DEFAULT_FRAGMENT_SIZE}.
*
* @param fragmentSize The fragment size in bytes, or {@link C#LENGTH_UNSET} to disable
* fragmentation.
* @return This factory.
*/
public Factory setFragmentSize(long fragmentSize) {
this.fragmentSize = fragmentSize;
return this;
}
/**
* Sets the size of an in-memory buffer used when writing to a cache file. A zero or negative
* value disables buffering.
*
* <p>The default value is {@link CacheDataSink#DEFAULT_BUFFER_SIZE}.
*
* @param bufferSize The buffer size in bytes.
* @return This factory.
*/
public Factory setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
return this;
}
@Override
public DataSink createDataSink() {
return new CacheDataSink(Assertions.checkNotNull(cache), fragmentSize, bufferSize);
}
}
/** Thrown when an {@link IOException} is encountered when writing data to the sink. */
public static final class CacheDataSinkException extends CacheException {
public CacheDataSinkException(IOException cause) {
super(cause);
}
}
/** Default {@code fragmentSize} recommended for caching use cases. */
public static final long DEFAULT_FRAGMENT_SIZE = 5 * 1024 * 1024;
/** Default buffer size in bytes. */
@ -59,17 +131,6 @@ public final class CacheDataSink implements DataSink {
private long dataSpecBytesWritten;
private @MonotonicNonNull ReusableBufferedOutputStream bufferedOutputStream;
/**
* Thrown when IOException is encountered when writing data into sink.
*/
public static class CacheDataSinkException extends CacheException {
public CacheDataSinkException(IOException cause) {
super(cause);
}
}
/**
* Constructs an instance using {@link #DEFAULT_BUFFER_SIZE}.
*

View file

@ -17,9 +17,8 @@ package com.google.android.exoplayer2.upstream.cache;
import com.google.android.exoplayer2.upstream.DataSink;
/**
* A {@link DataSink.Factory} that produces {@link CacheDataSink}.
*/
/** @deprecated Use {@link CacheDataSink.Factory}. */
@Deprecated
public final class CacheDataSinkFactory implements DataSink.Factory {
private final Cache cache;

View file

@ -23,6 +23,7 @@ import com.google.android.exoplayer2.upstream.DataSink;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSourceException;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.FileDataSource;
import com.google.android.exoplayer2.upstream.TeeDataSource;
import com.google.android.exoplayer2.upstream.TransferListener;
@ -36,6 +37,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* A {@link DataSource} that reads and writes a {@link Cache}. Requests are fulfilled from the cache
@ -44,6 +46,167 @@ import java.util.Map;
*/
public final class CacheDataSource implements DataSource {
/** {@link DataSource.Factory} for {@link CacheDataSource} instances. */
public static final class Factory implements DataSource.Factory {
private @MonotonicNonNull Cache cache;
private DataSource.Factory cacheReadDataSourceFactory;
@Nullable private DataSink.Factory cacheWriteDataSinkFactory;
private CacheKeyFactory cacheKeyFactory;
private boolean cacheIsReadOnly;
@Nullable private DataSource.Factory upstreamDataSourceFactory;
@CacheDataSource.Flags private int flags;
@Nullable private CacheDataSource.EventListener eventListener;
public Factory() {
cacheReadDataSourceFactory = new FileDataSource.Factory();
cacheKeyFactory = CacheUtil.DEFAULT_CACHE_KEY_FACTORY;
}
/**
* Sets the cache that will be used.
*
* <p>Must be called before the factory is used.
*
* @param cache The cache that will be used.
* @return This factory.
*/
public Factory setCache(Cache cache) {
this.cache = cache;
return this;
}
/**
* Sets the {@link DataSource.Factory} for {@link DataSource DataSources} for reading from the
* cache.
*
* <p>The default is a {@link FileDataSource.Factory} in its default configuration.
*
* @param cacheReadDataSourceFactory The {@link DataSource.Factory} for reading from the cache.
* @return This factory.
*/
public Factory setCacheReadDataSourceFactory(DataSource.Factory cacheReadDataSourceFactory) {
this.cacheReadDataSourceFactory = cacheReadDataSourceFactory;
return this;
}
/**
* Sets the {@link DataSink.Factory} for generating {@link DataSink DataSinks} for writing data
* to the cache. Passing {@code null} causes the cache to be read-only.
*
* <p>The default is a {@link CacheDataSink.Factory} in its default configuration.
*
* @param cacheWriteDataSinkFactory The {@link DataSink.Factory} for generating {@link DataSink
* DataSinks} for writing data to the cache, or {@code null} to disable writing.
* @return This factory.
*/
public Factory setCacheWriteDataSinkFactory(
@Nullable DataSink.Factory cacheWriteDataSinkFactory) {
this.cacheWriteDataSinkFactory = cacheWriteDataSinkFactory;
this.cacheIsReadOnly = cacheWriteDataSinkFactory == null;
return this;
}
/**
* Sets the {@link CacheKeyFactory}.
*
* <p>The default is {@link CacheUtil#DEFAULT_CACHE_KEY_FACTORY}.
*
* @param cacheKeyFactory The {@link CacheKeyFactory}.
* @return This factory.
*/
public Factory setCacheKeyFactory(CacheKeyFactory cacheKeyFactory) {
this.cacheKeyFactory = cacheKeyFactory;
return this;
}
/**
* Sets the {@link DataSource.Factory} for upstream {@link DataSource DataSources}, which are
* used to read data in the case of a cache miss.
*
* <p>The default is {@code null}, and so this method must be called before the factory is used
* in order for data to be read from upstream in the case of a cache miss.
*
* @param upstreamDataSourceFactory The upstream {@link DataSource} for reading data not in the
* cache, or {@code null} to cause failure in the case of a cache miss.
* @return This factory.
*/
public Factory setUpstreamDataSourceFactory(
@Nullable DataSource.Factory upstreamDataSourceFactory) {
this.upstreamDataSourceFactory = upstreamDataSourceFactory;
return this;
}
/**
* Sets the {@link CacheDataSource.Flags}.
*
* <p>The default is {@code 0}.
*
* @param flags The {@link CacheDataSource.Flags}.
* @return This factory.
*/
public Factory setFlags(@CacheDataSource.Flags int flags) {
this.flags = flags;
return this;
}
/**
* Sets the {link EventListener} to which events are delivered.
*
* <p>The default is {@code null}.
*
* @param eventListener The {@link EventListener}.
* @return This factory.
*/
public Factory setEventListener(@Nullable EventListener eventListener) {
this.eventListener = eventListener;
return this;
}
@Override
public CacheDataSource createDataSource() {
Cache cache = Assertions.checkNotNull(this.cache);
@Nullable
DataSource upstreamDataSource =
upstreamDataSourceFactory != null ? upstreamDataSourceFactory.createDataSource() : null;
@Nullable DataSink cacheWriteDataSink;
if (cacheIsReadOnly) {
cacheWriteDataSink = null;
} else if (cacheWriteDataSinkFactory != null) {
cacheWriteDataSink = cacheWriteDataSinkFactory.createDataSink();
} else {
cacheWriteDataSink = new CacheDataSink.Factory().setCache(cache).createDataSink();
}
return new CacheDataSource(
cache,
upstreamDataSource,
cacheReadDataSourceFactory.createDataSource(),
cacheWriteDataSink,
flags,
eventListener,
cacheKeyFactory);
}
}
/** Listener of {@link CacheDataSource} events. */
public interface EventListener {
/**
* Called when bytes have been read from the cache.
*
* @param cacheSizeBytes Current cache size in bytes.
* @param cachedBytesRead Total bytes read from the cache since this method was last called.
*/
void onCachedBytesRead(long cacheSizeBytes, long cachedBytesRead);
/**
* Called when the current request ignores cache.
*
* @param reason Reason cache is bypassed.
*/
void onCacheIgnored(@CacheIgnoredReason int reason);
}
/**
* Flags controlling the CacheDataSource's behavior. Possible flag values are {@link
* #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} and {@link
@ -96,27 +259,6 @@ public final class CacheDataSource implements DataSource {
/** Cache ignored due to a request with an unset length. */
public static final int CACHE_IGNORED_REASON_UNSET_LENGTH = 1;
/**
* Listener of {@link CacheDataSource} events.
*/
public interface EventListener {
/**
* Called when bytes have been read from the cache.
*
* @param cacheSizeBytes Current cache size in bytes.
* @param cachedBytesRead Total bytes read from the cache since this method was last called.
*/
void onCachedBytesRead(long cacheSizeBytes, long cachedBytesRead);
/**
* Called when the current request ignores cache.
*
* @param reason Reason cache is bypassed.
*/
void onCacheIgnored(@CacheIgnoredReason int reason);
}
/** Minimum number of bytes to read before checking cache for availability. */
private static final long MIN_READ_BEFORE_CHECKING_CACHE = 100 * 1024;
@ -148,10 +290,11 @@ public final class CacheDataSource implements DataSource {
* reading and writing the cache.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in the cache.
* @param upstreamDataSource A {@link DataSource} for reading data not in the cache. If null,
* reading will fail if a cache miss occurs.
*/
public CacheDataSource(Cache cache, DataSource upstream) {
this(cache, upstream, /* flags= */ 0);
public CacheDataSource(Cache cache, @Nullable DataSource upstreamDataSource) {
this(cache, upstreamDataSource, /* flags= */ 0);
}
/**
@ -159,14 +302,15 @@ public final class CacheDataSource implements DataSource {
* reading and writing the cache.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in the cache.
* @param upstreamDataSource A {@link DataSource} for reading data not in the cache. If null,
* reading will fail if a cache miss occurs.
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR}
* and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0.
*/
public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags) {
public CacheDataSource(Cache cache, @Nullable DataSource upstreamDataSource, @Flags int flags) {
this(
cache,
upstream,
upstreamDataSource,
new FileDataSource(),
new CacheDataSink(cache, CacheDataSink.DEFAULT_FRAGMENT_SIZE),
flags,
@ -179,7 +323,8 @@ public final class CacheDataSource implements DataSource {
* before it is written to disk.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in the cache.
* @param upstreamDataSource A {@link DataSource} for reading data not in the cache. If null,
* reading will fail if a cache miss occurs.
* @param cacheReadDataSource A {@link DataSource} for reading data from the cache.
* @param cacheWriteDataSink A {@link DataSink} for writing data to the cache. If null, cache is
* accessed read-only.
@ -189,14 +334,14 @@ public final class CacheDataSource implements DataSource {
*/
public CacheDataSource(
Cache cache,
DataSource upstream,
@Nullable DataSource upstreamDataSource,
DataSource cacheReadDataSource,
@Nullable DataSink cacheWriteDataSink,
@Flags int flags,
@Nullable EventListener eventListener) {
this(
cache,
upstream,
upstreamDataSource,
cacheReadDataSource,
cacheWriteDataSink,
flags,
@ -210,7 +355,8 @@ public final class CacheDataSource implements DataSource {
* before it is written to disk.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in the cache.
* @param upstreamDataSource A {@link DataSource} for reading data not in the cache. If null,
* reading will fail if a cache miss occurs.
* @param cacheReadDataSource A {@link DataSource} for reading data from the cache.
* @param cacheWriteDataSink A {@link DataSink} for writing data to the cache. If null, cache is
* accessed read-only.
@ -221,7 +367,7 @@ public final class CacheDataSource implements DataSource {
*/
public CacheDataSource(
Cache cache,
DataSource upstream,
@Nullable DataSource upstreamDataSource,
DataSource cacheReadDataSource,
@Nullable DataSink cacheWriteDataSink,
@Flags int flags,
@ -235,10 +381,14 @@ public final class CacheDataSource implements DataSource {
this.ignoreCacheOnError = (flags & FLAG_IGNORE_CACHE_ON_ERROR) != 0;
this.ignoreCacheForUnsetLengthRequests =
(flags & FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS) != 0;
this.upstreamDataSource = upstream;
if (cacheWriteDataSink != null) {
this.cacheWriteDataSource = new TeeDataSource(upstream, cacheWriteDataSink);
if (upstreamDataSource != null) {
this.upstreamDataSource = upstreamDataSource;
this.cacheWriteDataSource =
cacheWriteDataSink != null
? new TeeDataSource(upstreamDataSource, cacheWriteDataSink)
: null;
} else {
this.upstreamDataSource = DummyDataSource.INSTANCE;
this.cacheWriteDataSource = null;
}
this.eventListener = eventListener;

View file

@ -20,7 +20,8 @@ import com.google.android.exoplayer2.upstream.DataSink;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.FileDataSource;
/** A {@link DataSource.Factory} that produces {@link CacheDataSource}. */
/** @deprecated Use {@link CacheDataSource.Factory}. */
@Deprecated
public final class CacheDataSourceFactory implements DataSource.Factory {
private final Cache cache;
@ -44,6 +45,7 @@ public final class CacheDataSourceFactory implements DataSource.Factory {
}
/** @see CacheDataSource#CacheDataSource(Cache, DataSource, int) */
@SuppressWarnings("deprecation")
public CacheDataSourceFactory(
Cache cache, DataSource.Factory upstreamFactory, @CacheDataSource.Flags int flags) {
this(