VideoCache
使用Swift实现的VideoCache
起因
直接使用AVPlayer
播放视频,在用户切换4G时,只能销毁AVPlayer
,在用户点击允许播放之后,重新创建,用户体验比较差。因此考虑如何接管AVPlayer
的数据加载,在用户切换到4G环境时,只是暂停下载,缓存的数据仍然可用,也无需销毁AVPlayer
,体验会更流畅。
选择
查阅了AVPlayer
的文档,官方提供了处理数据加载的很好方案。对比了网上各种方案后,觉得官方方案比较简洁。接下来进行尝试。
基本步骤
步骤比较简单,分为如下三步:一、对要播放的URL进行处理,将schema
修改为自定义形式。这样,AVPlayer
就会将这个URL
的请求转交给我们。二、自定义一个类,实现AVAssetResourceLoaderDelegate
协议,重点实现这个协议中的两个方法。optional public func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool
和optional public func resourceLoader(_ resourceLoader: AVAssetResourceLoader, didCancel loadingRequest: AVAssetResourceLoadingRequest)
。三、将资源代理设置给AVURLAsseturlAsset.resourceLoader.setDelegate(delegate, queue: .main)
。
具体实现
三个步骤中,一和三比较简单,下面详细说明步骤二。其中包含三个关键的点。
关键点1:在播放前,AVPlayer
会通过func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool
这个函数询问是否等待加载数据,并将请求转交给我们。我们将返回true,并将请求添加到请求队列中,然后启动下载。当数据返回时,我们调用loadingRequestdataRequest.respond(with: data)
方法,将数据添加到request
中。当request
所需的数据全部提供后,我们通过loadingRequest.finishLoading()
向request
表示,数据已完全加载好,AVPlayer
接着可以拿数据进行播放。
关键点2:其中第一次的数据加载,仅请求前两个字节,然后根据返回信息,填充AVAssetResourceLoadingRequest
的contentInformationRequest
,以告知AVPlayer
视频的大小、是否支持byteRange
以及视频格式。
关键点3:func resourceLoader(_ resourceLoader: AVAssetResourceLoader, didCancel loadingRequest: AVAssetResourceLoadingRequest)
在进行seek操作时,这个方法会频繁调用,告知之前的请求已被取消,因为seek之后需要下载选中的片段,因此之前片段的下载需要被停止。此时我们需要取消之前的数据请求,否则会浪费用户流量。