更新:
我发现了this post,其中详细说明了我所遇到的相同问题。事实证明,事实是我在DocumentsProvider中使用Pipe方法从DropBox流传输内容,这意味着ExoPlayer事先不知道文件的大小,因此默认情况下不会将其保存到缓存中。 >
所以我最终做了我认为作者所做的事情-我为这些情况创建了一个自定义CacheDataSource,它更改了该类的open()方法中的DataSpec.flags变量:
public long open(DataSpec dataSpec) throws IOException {
try {
key = cacheKeyFactory.buildCacheKey(dataSpec);
uri = dataSpec.uri;
actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri);
httpMethod = dataSpec.httpMethod;
if ( !dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH) ) { // <-- update here
flags = (dataSpec.flags | DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH);
} else {
flags = dataSpec.flags;
}
readPosition = dataSpec.position;
不是最佳解决方案,我在另一篇文章中也提出了请求,要求提供一种更受支持的方式来指示应设置此标志。
但是至少现在我的流式文件已保存在缓存中。
我正在为ExoPlayer2实现一个客户CacheDataSourceFactory,以便实现缓存来存储流到ExoPlayer的视频。
我在这里查看了几篇文章,this one有助于正确地采用通用方法将视频缓存到我选择的目录中。
我注意到,当处理解析为我的自定义DocumentsProvider的URI时,由CacheDataSourceFactory定义的Cache仅用于存储看起来像“指针”或“索引”文件(“ cached_content_index.exi”)的内容。在该文件中,我看到了我的自定义DocumentsProvider流式传输的视频的URI。但是,实际的视频不在缓存中。
这是我的提供商的相关部分,很简单:
// Return a descriptor that will stream the file
Timber.d("In openDocument of DropboxProvider for Id: %s, streaming from source", documentId);
ParcelFileDescriptor[] pipe;
try {
pipe = ParcelFileDescriptor.createPipe();
// Get input stream for the pipe
DbxDownloader downloader = mDbxClient.files().download(fileMetadata.getPathLower(), fileMetadata.getRev());
new TransferThread(downloader.getInputStream(), new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]), signal, fileMetadata.getSize()).start();
return pipe[0];
} catch (DbxException dbe) {
Timber.d("Got IDbxException when streaming content: %s", dbe.getMessage());
} catch (IOException ioe) {
Timber.d("Got IOException when streaming content: %s", ioe.getMessage());
} catch (Exception e) {
Timber.d("Got Exception when streaming content: %s", e.getMessage());
}
return null;
还有TransferThread:
private static class TransferThread extends Thread {
final InputStream in;
final OutputStream out;
final CancellationSignal signal;
final long size;
TransferThread(InputStream in, OutputStream out, CancellationSignal signal, long size) {
this.in = in;
this.out = out;
this.signal = signal;
this.size = size;
}
@Override
public void run() {
int biteSize = (8*1024);
if ( size <= (biteSize * 8) ) {
biteSize = Math.max( ((int)(size / (biteSize*2))) * (biteSize * 2), biteSize);
}
Timber.d("TransferThread: File size is: %s, buffer biteSize set to: %d", InTouchUtils.getFormattedFileSize(size), biteSize);
byte[] buf = new byte[biteSize];
int len;
try {
while ( ((len=in.read(buf)) >= 0) && (signal == null || !signal.isCanceled()) ) {
out.write(buf, 0, len);
}
} catch (IOException e) {
// When Glide is used to request a URI where this provider resolves the query,
// it will close the stream out from under us once it has fetched enough bytes
// to render a single frame as an image if the if it is to a video, so
// we swallow that exception here, only logging the error if it isn't that EPIPE
// (broken pipe due to one end being closed) exception.
if ( !(e.getMessage().contains("EPIPE"))) {
Timber.d("TransferThread: Got IOException transferring file: %s", e.getMessage());
}
} finally {
try {
if (in != null) {
in.close();
}
if ( out != null ) {
out.flush();
out.close();
}
Timber.d("TransferThread: Finished streaming file.");
} catch (IOException ioe) {
Timber.d("TransferThread: Got IOException closing file: %s", ioe.getMessage());
}
}
}
}
再次-在这种情况下,ExoPlayer对它从DocumentsProvider接收到的ParcelFileDescriptor似乎很满意-它占用流式传输到它的字节并播放视频。我只是没有看到视频文件最终出现在缓存中。
我还尝试了一个示例,该示例从我的Google云端硬盘(使用SAF的现成文档提供程序)流式传输视频,而这次视频确实在缓存中结束了。
由于它们都使用相同的MediaSource实例-Google Docs提供程序必须采用某种方法,以便ExoPlayer知道将生成的流视频放入我的自定义Dropbox DocumentsProvider未做的缓存中。
有人知道如何获取SAF附带的DocumentsProvider的源代码,该SAF管理对Google Docs的访问?我想在它的openDocument()方法中查看它的作用。
Dropbox提供程序是否正在利用其ParcelFileDescriptor中的Pipe来执行ExoPlayer无法处理的事情?
其他想法?