我想通过AVFoundation
在iOS中集成离线HLS。
我有一个带有简单AES-128的加密HLS,并且它不想在离线模式下播放,我试图整合AVAssetResourceLoaderDelegate
但不知道如何整合applicationCertificate
& ; https://developer.apple.com/streaming/fps/示例中的contentKeyFromKeyServerModuleWithSPCData
。我有一种感觉,我做错了,这是一个样本AES-128 encryption
,甚至不是DRM
。
如果没有互联网,AVPlayer
仍在尝试encryption key
通过GET
请求。
如果有人成功地在本地保存加密密钥并以某种方式将AVPlayer
与AVURLAsset
一起保存,那就太棒了。
有人设法整合这个吗?
答案 0 :(得分:11)
我写过苹果支持,他们的回答对我来说并不新鲜。在我开始与他们对话之前,我从wwdc视频和文档中获得了他们提供给我的信息。 (https://developer.apple.com/streaming/fps/)
此外,我将介绍如何使用AES-128加密在离线模式下播放HLS。 注意AVDownloadTask在模拟器上不起作用,因此您应该有一个用于此实现的设备。 首先,您需要一个流网址。
第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()
其中:response - >来自URLSession的响应,modifiedData - >已更改网址的数据
恢复下载任务并在 shouldWaitForLoadingOfRequestedResource 委托
中返回true第4步: 如果一切正常,AVAssetDownloadDelegate将触发:
- (void)URLSession:(NSURLSession *)session assetDownloadTask:(AVAssetDownloadTask *)assetDownloadTask didResolveMediaSelection:(AVMediaSelection *)resolvedMediaSelection NS_AVAILABLE_IOS(9_0) {
}
第5步: 当AVFoundation选择最佳媒体流网址时,我们已将所有<em> https 更改为 fakehttps , shouldWaitForLoadingOfRequestedResource 将再次使用来自第一个.m3u8的网址之一触发
第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”
我们有加密密钥的网址
第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()
其中:response - &gt;来自URLSession的响应,modifiedData - &gt;已更改网址的数据
恢复下载任务并在shouldWaitForLoadingOfRequestedResource委托中返回true(与步骤3相同)
第9步:
当然,当下载任务尝试创建具有修改后的URI=
的请求时,再次不是有效的 shouldWaitForLoadingOfRequestedResource 将再次触发。在这种情况下,您应该检测到这一点并使用您的持久密钥(您在本地保存的密钥)创建新数据。请注意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) {
}
您应该在某处保存location
,当您想要从设备播放流时,您应该从此location
网址
所有这些信息都由AVFoundation在本地保存,因此下次您尝试在离线状态下播放本地内容AVURLAsset会因为URI = fakehttps://avid.avid.net/avid/key
而调用委托,这是一个无效的链接,在这里您将执行步骤再次9,视频将以离线模式播放。
这对我有用,如果有人知道更好的实施,我会很高兴知道。