ChangbaDevs / KTVHTTPCache
- четверг, 30 ноября 2017 г. в 03:13:35
A media cache framework from Changba iOS Team.
唱吧 iOS 团队为了解决音视频在线播放的缓存问题,开发了 KTVHTTPCache 这个框架。设计之初是为了解决音视频在线播放的缓存问题,但其本质是对 HTTP 请求进行缓存,对传输内容并没有限制,因此应用场景不限于音视频在线播放,也可以用于文件下载、图片加载、普通网络请求等场景。
对于有重度音视频在线播放需求的应用,缓存无疑是必不可少的功能。目前常用的方案有 Local HTTP Server 和 AVAssetResourceLoader 两种。二者实现及原理虽有不同,但本质都是要 Hook 到播放器资源加载的请求,从而接管资源加载逻辑。根据缓存状态,自行决定是否需要通过网络加载资源。从应用场景的角度看,二者有一个比较大的差异是前者可以搭配任意前端播放器,而后者只能配合 AVPlayer 使用。
个人认为,由于 AVAssetResourceLoader 是黑盒且会干预 AVPlayer 本身的播放逻辑,导致坑多且难排查。并且不同的版本之间会有行为差异(例如近期发现在最新的 iOS 11 系统中,原本工作正常的代码,因为一个细小的行为变化,引发了一个 Bug),去适配它的逻辑会有不小的工作量。相反 Local HTTP Server 是完全 Open Source,我们能够全面接管资源加载逻辑,可以尽可能的规避缓存策略的引入带来的风险。
KTVHTTPCache 由 HTTP Server 和 Data Storage 两大模块组成。前者负责与 Client 交互,后者负责资源加载及缓存处理。为方便拓展,Data Storage 为独立模块,也可直接与 Client 交互(例如可与 AVAssetResourceLoader 配合使用)。
以网络使用最小化为原则,设计了分片加载数据的功能。有 Network Source 和 File Source 两种用于加载数据的 Source,分别用于下载网络数据和读取本地数据。通过分析 Data Request 的 Range 和本地缓存状态来对应创建。
例如一次请求的 Range 为 0-999,本地缓存中已有 200-499 和 700-799 两段数据。那么会对应生成 5 个 Source,分别是:
它们由 Data Sourcer 进行管理,对外仅暴露一个 Read Data 的接口,根据当前的 Read Offset 自行选择向外界提供数据的 Source。
// 使用简单,基本可以忽略集成成本
// 启动(全局启动一次即可)
NSError * error;
[KTVHTTPCache proxyStart:&error];
// 使用
NSString * URLString = [KTVHTTPCache proxyURLStringWithOriginalURLString:@"原始 URL"];
AVPlayer * player = [AVPlayer playerWithURL:[NSURL URLWithString:URLString]];
在音视频缓存上,我们一共采用过如下 4 个方案:
AVPlayer 在播放时会优先根据 Response Header 中的 Content-Type 判断当前资源是否可以播放。当 Content-Type 无法给出有效信息时再去判断 URL 中的 Path Extension。
对应关系如下:
URL | Content-Type | 是否可播 |
---|---|---|
http://changba.com/video.mp4 | video/mp4 | YES |
http://changba.com/video.mp4 | application/octet-stream | YES |
http://changba.com/video | video/mp4 | YES |
http://changba.com/video | application/octet-stream | NO |
因此要想让 AVPlayer 正常播放,Content-Type 和 Path Extension 中至少能提供一个有效信息,否则将直接报 Error。
在本地 Server 中有一个 Socket 用于接收 AVPlayer 发出的请求。如果在 AVPlayer 为非播放状态时锁屏,一段时间后再唤起 App,FD 虽然还在,但 Listen 的端口会被回收,导致 FD 接收不到事件,AVPlayer 发出的请求也就无法被本地 Server 接收到。我们的解决办法是在做 URL 映射时 Ping 一下本地 Server,如果 Ping 不通,会重启本地 Server。
项目已经开源,GitHub 地址: https://github.com/ChangbaDevs/KTVHTTPCache
对重度影音类应用而言,音视频缓存属于比较重要的一环,对稳定性也有比较高的要求,我们在这上走过一些弯路、踩过一些坑。希望 KTVHTTPCache 的开源能给大家带来一些帮助。也非常欢迎大家在项目中使用,如果遇到问题可以在 GitHub 提 Issue 给我。