使用AES-128encryptioniOS播放离线HLS

我想通过AVFoundation在iOS中集成脱机HLS。 我有一个简单的AES-128encryption的HLS,它不想在离线模式下播放,我试图集成AVAssetResourceLoaderDelegate但不知道如何集成https://developer.apple中的 applicationCertificatecontentKeyFromKeyServerModuleWithSPCData 。 com / streaming / fps / examples。 我有一种感觉,我做错了,它是一个AES-128 encryption样本,甚至不是DRM

没有互联网, AVPlayer仍然试图通过GET请求获得encryption key 。 如果有人成功地将encryption的密钥保存在本地,并以某种方式将其与AVURLAsset一起交给AVPlayer ,那AVPlayer AVURLAsset

有人设法整合这个?

我已经写信给苹果支持,他们的回应对我来说并不新鲜。 他们提供给我的信息在我开始与他们交谈之前,我从wwdc的video和文档中获得了信息。 ( https://developer.apple.com/streaming/fps/

此外,我将介绍如何在离线模式下使用AES-128encryption来实现HLS播放。 注意AVDownloadTask在模拟器上不起作用,所以你应该有一个这个实现的设备。 开始时,您需要一个stream式URL。

步骤1:在创buildAVURLAsset之前,我们应该将streamURL和更改scheme转换为无效URL(例如: https – > fakehttps ,通过URLComponents完成),并将AVAssetResourceLoaderDelegate分配给新创build的url资源。 所有这些改变迫使AVAssetDownloadTask调用:

 func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool { 

(这是调用,因为AVFoundation看到一个无效的URL,不知道该怎么办)

第二步:当代理被调用时,我们应该检查那个URL是我们以前有的那个。 我们需要改回有效的scheme,并创build一个简单的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步:parsing所有这些数据所需的信息,并将所有的httpsscheme更改为无效的一个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 – >具有更改的URL的数据

恢复您的下载任务,并在shouldWaitForLoadingOfRequestedResource委托中返回true

第4步:如果一切都会好的AVAssetDownloadDelegate将会触发:

 - (void)URLSession:(NSURLSession *)session assetDownloadTask:(AVAssetDownloadTask *)assetDownloadTask didResolveMediaSelection:(AVMediaSelection *)resolvedMediaSelection NS_AVAILABLE_IOS(9_0); 

第5步:当AVFoundation将select最佳的媒体streamURL时,我们已将所有的https更改为fakehttpsshouldWaitForLoadingOfRequestedResource将再次以其中一个URL触发.m3u8

第6步:当再次调用委托时,我们应该检查那个url是我们需要的。 再次将假的scheme更改为有效的scheme,并使用此URL创build一个简单的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步:parsing第二个.m3u8文件,并从中获取所需的所有信息,也请参阅

 #EXT-X-KEY:METHOD=AES-128,URI="https://avid.avid.net/avid/key” 

我们有encryption密钥的URL

第8步:在将一些信息发送回AVAssetDownloadDelegate之前,我们需要从服务器下载密钥并将其保存在本地设备上。 在此之后,您应该将URI = https://avid.avid.net/avid/key从第二个.m3u8更改为无效的URI = fakehttps://avid.avid.net/avid/key ,或者可能是一个本地文件path你已经保存了你的本地密钥。 现在你应该从shouldWaitForLoadingOfRequestedResource委托设置AVSsetResourceLoadingRequest。 喜欢:

 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 – >具有更改的URL的数据

继续下载任务并在shouldWaitForLoadingOfRequestedResource委托中返回true(与第3步相同)

第9步:当然,当下载任务将尝试创build具有修改后的URI=请求时,这个再次不是有效的请求,那么shouldWaitForLoadingOfRequestedResource将再次触发。 在这种情况下,你应该检测这个并用你的持久性键(你在本地保存的键值)创build新的数据。在这里注意, 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地方,当你想从设备播放stream应该创buildAVURLAsset从这个location URL

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

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