使用AES-128加密iOS播放离线HLS

时间:2017-08-14 08:53:18

标签: ios encryption hls

我想通过AVFoundation在iOS中集成离线HLS。 我有一个带有简单AES-128的加密HLS,并且它不想在离线模式下播放,我试图整合AVAssetResourceLoaderDelegate但不知道如何整合applicationCertificate& ; https://developer.apple.com/streaming/fps/示例中的contentKeyFromKeyServerModuleWithSPCData。我有一种感觉,我做错了,这是一个样本AES-128 encryption,甚至不是DRM

如果没有互联网,AVPlayer仍在尝试encryption key通过GET请求。 如果有人成功地在本地保存加密密钥并以某种方式将AVPlayerAVURLAsset一起保存,那就太棒了。

有人设法整合这个吗?

1 个答案:

答案 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网址

创建AVURLAsset

所有这些信息都由AVFoundation在本地保存,因此下次您尝试在离线状态下播放本地内容AVURLAsset会因为URI = fakehttps://avid.avid.net/avid/key而调用委托,这是一个无效的链接,在这里您将执行步骤再次9,视频将以离线模式播放。

这对我有用,如果有人知道更好的实施,我会很高兴知道。