0%

媒体缓存设计优化

JOOX作为一个媒体类的应用,大量的场景用到音乐和mv等媒体播放,对媒体的缓存依赖高。所以需要做媒体缓存,对于已经播放和歌曲做到边下边播,未播放的歌曲能做预加载,提升用户体验,减少用户等待耗时。

1 AndroidVideoCache 媒体缓存框架

设计

AndroidVideoCache 通过代理的策略实现一个中间层将我们的网络请求转移到本地实现的代理服务器上,这样我们真正请求的数据就会被代理拿到,这样代理一边向本地写入数据,一边根据我们需要的数据看是读网络数据还是读本地缓存数据再提供给我们,真正做到了数据的复用。
这就和我们使用的抓包软件性质一样,上个原理图更清晰:
video_cache_design

在视频播放器,发起一个urlA请求时,通过HttpProxyCacheServer转成一个本地host和端口的urlB,这样视频播放器发起请求就是向HttpProxyCacheServer请求,返回视频播放器的Socket,Server再建立一个HttpProxyCacheServerClients来发起网络请求处理缓存等工作,然后把数据通过前面的Socket返回给视频播放器。

缺点

对于长歌曲类的媒体,需要支持seek操作,seek操作播放是通过重新发起http请求,通过请求头设置range的方式重新请求数据,这个时候整首歌曲文件是不完整的。
为了保证缓存文件的完整性,在遇到seek的场景时,缓存框架服务不会通过rang的方式请求,而是从0开始请求下载数据,直到下载seek的position时候才将数据返回,这样在非常影响播放体验,导致seek加载过长。可以看看ProxyCacheread方法,可以看看读取的这里会一直等待下载足够的数据。

1
2
3
4
5
6
7
8
9
public int read(byte[] buffer, long offset, int length) throws ProxyCacheException {
while (!cache.isCompleted() && cache.available() < (offset + length) && !stopped) {
readSourceAsync();
waitForSourceData();
checkReadSourceErrorsCount();
}
int read = cache.read(buffer, offset, length);
return read;
}

2 媒体缓存框架优化

分块缓存的设计

后面我们调研官方推荐exoplayer的时候,发现exoplayer内也有一套缓存设计,这套缓存设计思路非常新颖,因为视频播放常用的seek操作,所以从最初设计就考虑到这个问题,这个框架采用了分块缓存的方式。那整体的缓存结构设计将会更加复杂,需要管理key、position、length和文件续读问题。具体的实现逻辑这里不深入分析,可以看源码,最核心的类是CacheDataSource
video_cache_info
我们可以看看分块文件的生成命名CacheSpan

1
2
3
4
public static File getCacheFileName(File cacheDir, String key, long offset, long totalLength, FileType fileType,
long lastAccessTimestamp) {
return new File(cacheDir, key + "." + offset + "." + totalLength + "." + fileType.encode() + "." + lastAccessTimestamp + SUFFIX);
}

其实这个我带着部分疑问点进入下一步的:

  • 因为是分块文件管理,那可能存在当缓存满的时候,歌曲中间的文件被删掉,那缓存的读取过程是什么样的
  • 因为可能有seek操作,那该首歌曲前面一部分没有缓存数据,后面一部分有缓存数据,那会怎么处理。

场景分析-歌曲缓存文件都存在

在 CacheDataSource open过程中,它会打开一个读取流,有缓存的时候直接读取本地文件,这块没什么问题,接着当第一个片段读取完成后,需要续接上第二个片段,这个时候重新开启了一个读取流,核心就在readopenNextSource中。

场景分析-歌曲缓存文件中段缺失

继续看CacheDataSource的read方法,当第一个片段读取完成后,会开启一下个读取流openNextSource,这个时候本地无缓存文件,将会开启网络流读取数据,读取的长度是当前去读的缓存position到下一段缓存开始的位置,也就是读取中间空缺的数据。
media_read_design1

场景分析-歌曲缓存文件前段缺失

其实原理和上面讲的差不多,先请求缺失部分长度的网络数据。
media_read_design2

优劣分析

从图中可以清晰封分析出分段缓存优势更多。

  • 分段缓存,缓存效率和命中率更高。
  • 分段缓存,在seek的情况下完美支持。

缺点:

  • 部分场景可能一个文件需要创建多次http请求,有些许影响性能。但是基于现状的网络条件,基本都使用了keep-alive和http2.0,通过连接池复用上避免了此类问题。
  • exoplayer中的缓存是强耦合,无法直接拿出来用。

总结

虽然分块缓存的方式比较好,但是exoplayer的缓存逻辑是强耦合在播放器里面,腾讯基于上述两种开源框架,将代理和分块结合,研发了新的视频框架oskplayer。整体缓存命中率提高了**%。