2

更新:

我找到了这篇文章,其中详细说明了我所看到的完全相同的问题。事实证明,我在 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 的视频。

我在这里查看了几篇文章,这篇文章有助于获得将视频缓存到我选择的目录中的一般方法。

我注意到在处理解析为我的自定义 DocumentsProvider 的 URI 时,CacheDataSourceFactory 定义的缓存仅用于存储看起来像“指针”或“索引”文件(“cached_content_index.exi”)的内容。在该文件中,我看到了由我的自定义 DocumentsProvider 流式传输的视频的 URI。但是实际的视频不在缓存中。

这是我的 Provider 的相关部分,它非常简单:

        // 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 Drive 流式传输视频(它使用 SAF 的开箱即用文档提供程序),这一次视频确实在缓存中结束。

由于它们都使用相同的 MediaSource 实例 - Google Docs 提供程序必须采用一种方法,以便 ExoPlayer 知道将生成的流式视频放置在我的自定义 Dropbox DocumentsProvider 没有执行的缓存中。

有谁知道如何获取管理对 Google Docs 访问权限的 SAF 附带的 DocumentsProvider 的源代码?我想看看它在 openDocument() 方法中做了什么。

Dropbox 提供商在其 ParcelFileDescriptor 中使用管道这一事实是 ExoPlayer 无法处理的吗?

其他想法?

4

0 回答 0