我写过苹果支持,他们的回答对我来说并不新鲜.在我开始与他们对话之前,我从wwdc视频和文档中获得了他们提供给我的信息. (
https://developer.apple.com/streaming/fps/)
此外,我将介绍如何使用AES-128加密在离线模式下播放HLS.
注意AVDownloadTask在模拟器上不起作用,因此您应该有一个用于此实现的设备.
首先,您需要一个流URL.
步骤1:
在创建AVURLAsset之前,我们应该将流URL和更改方案更改为无效的(例如:https – > fakehttps,我是通过URLComponents完成的)并将AVAssetResourceLoaderDelegate分配给新创建的url资产.所有这些更改都会强制AVAssetDownloadTask调用:
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
}
(它正在调用因为AVFoundation看到无效的URL并且不知道如何处理它)
第2步:
当委托被调用时,我们应该检查那个url是我们之前的那个url.我们需要将方案更改为有效方案并使用它创建一个简单的URLSession.我们将得到第一个.m3u8文件,应该是这样的:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1697588,RESOLUTION=1280x720,FRAME-RATE=23.980,CODECS="mp4a"
https://avid.avid.net/avid/information_about_stream1
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1132382,RESOLUTION=848x480,FRAME-RATE=23.980,CODECS="mp4a"
https://avid.avid.net/avid/information_about_stream2
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=690409,RESOLUTION=640x360,FRAME-RATE=23.980,CODECS="mp4a"
https://avid.avid.net/avid/information_about_stream3
第3步:
从此数据中解析所有需要的信息,并将所有https方案更改为无效的一个fakehttps
现在你应该从shouldWaitForLoadingOfRequestedResource委托设置AVAssetResourceLoadingRequest,如:
loadingRequest.contentInformationRequest?.contentType = response.mimeType
loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
loadingRequest.contentInformationRequest?.contentLength = response.expectedContentLength
loadingRequest.dataRequest?.respond(with: modifiedData)
loadingRequest.finishLoading()
downloadTask?.resume()
其中:响应 – >来自URLSession的响应,modifiedData – >已更改网址的数据
恢复下载任务并在shouldWaitForLoadingOfRequestedResource委托中返回true
第4步:
如果一切正常,AVAssetDownloadDelegate将触发:
- (void)URLSession:(NSURLSession *)session assetDownloadTask:(AVAssetDownloadTask *)assetDownloadTask didResolveMediaSelection:(AVMediaSelection *)resolvedMediaSelection NS_AVAILABLE_IOS(9_0) {
}
第5步:
当AVFoundation选择最佳媒体流URL时,我们已将所有https更改为fakehttps,shouldWaitForLoadingOfRequestedResource将再次使用来自第一个.m3u8的URL之一触发
第6步:
当再次调用委托时,我们应该检查url是否是我们需要的那个.将伪方案再次更改为有效方案,并使用此URL创建一个简单的URLSession.我们将获得第二个.m3u8文件:
#EXTM3U
#EXT-X-TARGETDURATION:12
#EXT-X-ALLOW-CACHE:YES
#EXT-X-KEY:METHOD=AES-128,URI="https://avid.avid.net/avid/key”
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:6.006,
https://avid.avid.net/avid/information_about_stream1
#EXTINF:4.713,
https://avid.avid.net/avid/information_about_stream2
#EXTINF:10.093,
https://avid.avid.net/avid/information_about_stream3
#EXT-X-ENDLIST
第7步:
解析第二个.m3u8文件并从中获取所需的所有信息,同时查看
#EXT-X-KEY:METHOD=AES-128,URI="https://avid.avid.net/avid/key”
我们有加密密钥的URL
第8步:
在将一些信息发送回AVAssetDownloadDelegate之前,我们需要从服务器下载密钥并将其本地保存在设备上.在此之后,您应该将URI = https://avid.avid.net/avid/key从第二个.m3u8更改为无效的URI = fakehttps://avid.avid.net/avid/key,或者可能是本地文件路径,其中您已保存本地密钥.
现在你应该从shouldWaitForLoadingOfRequestedResource委托smth设置AVAssetResourceLoadingRequest.喜欢:
loadingRequest.contentInformationRequest?.contentType = response.mimeType
loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
loadingRequest.contentInformationRequest?.contentLength = response.expectedContentLength
loadingRequest.dataRequest?.respond(with: modifiedData)
loadingRequest.finishLoading()
downloadTask?.resume()
其中:响应 – >来自URLSession的响应,modifiedData – >已更改网址的数据
恢复下载任务并在shouldWaitForLoadingOfRequestedResource委托中返回true(与步骤3相同)
第9步:
当然,当下载任务尝试使用修改后的URI =创建请求时,再次不是有效的请求,应该再次触发.WaitForLoadingOfRequestedResource.在这种情况下,您应该检测到这一点并使用您的持久密钥(您在本地保存的密钥)创建新数据.请注意,这里的contentType应该是AVStreamingKeyDeliveryPersistentContentKeyType,而不是它AVFoundation不理解这包含密钥).
loadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryPersistentContentKeyType
loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
loadingRequest.contentInformationRequest?.contentLength = keyData.count
loadingRequest.dataRequest?.respond(with: keyData)
loadingRequest.finishLoading()
downloadTask?.resume()
第10步:
块将由AVFoudnation自动下载.
下载完成后,将调用此委托:
func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) {
}
您应该在某处保存位置,当您想要从设备播放流时,您应该从此位置URL创建AVURLAsset
所有这些信息都由AVFoundation本地保存,因此下次当您尝试在离线AVURLAsset中播放本地内容时,将调用委托,因为URI = fakehttps://avid.avid.net/avid/key,这是一个无效的链接,在这里,您将再次执行步骤9,视频将以离线模式播放.
这对我有用,如果有人知道更好的实现,我会很高兴知道.