使用 AES-128 加密播放离线 HLS iOS
Playing Offline HLS with AES-128 encryption iOS
我想通过 AVFoundation
在 iOS 中集成离线 HLS。
我有一个带有简单 AES-128 的加密 HLS,它不想在离线模式下播放,我试图集成 AVAssetResourceLoaderDelegate
,但不知道如何集成 applicationCertificate
和 contentKeyFromKeyServerModuleWithSPCData
在 https://developer.apple.com/streaming/fps/ 个示例中。我有一种感觉,我做错了什么。这是一个样本AES-128 encryption
,甚至不是DRM
。
没有互联网,AVPlayer
仍在尝试通过 GET
请求获得 encryption key
。
如果有人成功地将加密密钥保存在本地并以某种方式将其与 AVURLAsset
.
一起提供给 AVPlayer
,那就太好了
有人成功整合了吗?
我已经写信给苹果支持,他们的回复对我来说并不新鲜。在我开始与他们对话之前,我从 wwdc 视频和文档中获得了他们提供给我的信息。 (https://developer.apple.com/streaming/fps/)
另外,我将描述我是如何使用AES-128加密实现离线播放HLS的。 Example On Github 描述了以下过程。
注意 AVDownloadTask 在模拟器上不起作用,因此您应该有一个用于此实现的设备。
一开始,你需要一个流URL.
第 1 步:
在创建 AVURLAsset 之前,我们应该获取流 URL 并将方案更改为无效方案(例如:https -> fakehttps,我是通过 URLComponents) 完成的,并将 AVAssetResourceLoaderDelegate 分配给新创建的 url 资产。所有这些变化都迫使 AVAssetDownloadTask 调用:
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
}
(它调用是因为 AVFoundation 看到一个无效的 URL 并且不知道如何处理它)
第 2 步:
当委托被调用时,我们应该检查 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()
其中:响应 -> 来自 URL 会话的响应,已修改的数据 -> 已更改 URL 的
的数据
恢复您的下载任务并在 shouldWaitForLoadingOfRequestedResource 委托
中 return 为真
第 4 步:
如果一切正常,AVAssetDownloadDelegate 将触发:
- (void)URLSession:(NSURLSession *)session assetDownloadTask:(AVAssetDownloadTask *)assetDownloadTask didResolveMediaSelection:(AVMediaSelection *)resolvedMediaSelection NS_AVAILABLE_IOS(9_0) {
}
第 5 步:
当 AVFoundation 将 select 最佳媒体流 URL、shouldWaitForLoadingOfRequestedResource 时,我们已将所有 https 更改为 fakehttps 将使用第一个 .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()
其中:响应 -> 来自 URL 会话的响应,已修改的数据 -> 已更改 URL 的
的数据
恢复您的下载任务并且 return 在 shouldWaitForLoadingOfRequestedResource 委托中为真(与步骤 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
URL
创建 AVURLAsset
所有这些信息都由 AVFoundation 保存在本地,因此下次当您尝试在离线 AV 中播放本地内容时URL由于 URI=fakehttps://avid.avid.net/avid/key
,资产委托将被调用,这是一个无效link,这里您将再次执行第 9 步,视频将以离线模式播放。
如果有人知道更好的实施方式,我会很高兴知道这对我有用。
我想通过 AVFoundation
在 iOS 中集成离线 HLS。
我有一个带有简单 AES-128 的加密 HLS,它不想在离线模式下播放,我试图集成 AVAssetResourceLoaderDelegate
,但不知道如何集成 applicationCertificate
和 contentKeyFromKeyServerModuleWithSPCData
在 https://developer.apple.com/streaming/fps/ 个示例中。我有一种感觉,我做错了什么。这是一个样本AES-128 encryption
,甚至不是DRM
。
没有互联网,AVPlayer
仍在尝试通过 GET
请求获得 encryption key
。
如果有人成功地将加密密钥保存在本地并以某种方式将其与 AVURLAsset
.
AVPlayer
,那就太好了
有人成功整合了吗?
我已经写信给苹果支持,他们的回复对我来说并不新鲜。在我开始与他们对话之前,我从 wwdc 视频和文档中获得了他们提供给我的信息。 (https://developer.apple.com/streaming/fps/)
另外,我将描述我是如何使用AES-128加密实现离线播放HLS的。 Example On Github 描述了以下过程。 注意 AVDownloadTask 在模拟器上不起作用,因此您应该有一个用于此实现的设备。 一开始,你需要一个流URL.
第 1 步: 在创建 AVURLAsset 之前,我们应该获取流 URL 并将方案更改为无效方案(例如:https -> fakehttps,我是通过 URLComponents) 完成的,并将 AVAssetResourceLoaderDelegate 分配给新创建的 url 资产。所有这些变化都迫使 AVAssetDownloadTask 调用:
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
}
(它调用是因为 AVFoundation 看到一个无效的 URL 并且不知道如何处理它)
第 2 步: 当委托被调用时,我们应该检查 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()
其中:响应 -> 来自 URL 会话的响应,已修改的数据 -> 已更改 URL 的
的数据恢复您的下载任务并在 shouldWaitForLoadingOfRequestedResource 委托
中 return 为真第 4 步: 如果一切正常,AVAssetDownloadDelegate 将触发:
- (void)URLSession:(NSURLSession *)session assetDownloadTask:(AVAssetDownloadTask *)assetDownloadTask didResolveMediaSelection:(AVMediaSelection *)resolvedMediaSelection NS_AVAILABLE_IOS(9_0) {
}
第 5 步: 当 AVFoundation 将 select 最佳媒体流 URL、shouldWaitForLoadingOfRequestedResource 时,我们已将所有 https 更改为 fakehttps 将使用第一个 .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()
其中:响应 -> 来自 URL 会话的响应,已修改的数据 -> 已更改 URL 的
的数据恢复您的下载任务并且 return 在 shouldWaitForLoadingOfRequestedResource 委托中为真(与步骤 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
URL
所有这些信息都由 AVFoundation 保存在本地,因此下次当您尝试在离线 AV 中播放本地内容时URL由于 URI=fakehttps://avid.avid.net/avid/key
,资产委托将被调用,这是一个无效link,这里您将再次执行第 9 步,视频将以离线模式播放。
如果有人知道更好的实施方式,我会很高兴知道这对我有用。