首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是什么原因导致AVAssetCache为完全下载的资产报告不可脱机播放?

是什么原因导致AVAssetCache为完全下载的资产报告不可脱机播放?
EN

Stack Overflow用户
提问于 2018-07-26 21:53:21
回答 2查看 1.9K关注 0票数 6

我正在开发一个iOS应用程序,它通过HLS播放FairPlay加密的音频,并支持下载和流。在飞机模式下,我无法播放下载的内容。如果在下载完成时从本地URL创建AVURLAssetasset.assetCache.isPlayableOffline将返回NO,并且当我尝试在飞机模式下播放时,它仍然会尝试请求一个.m3u8播放列表文件。

我的主播放列表如下所示:

代码语言:javascript
复制
#EXTM3U
# Created with Bento4 mp4-hls.py version 1.1.0r623

#EXT-X-VERSION:5
#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES,URI="skd://url/to/key?KID=foobar",KEYFORMAT="com.apple.streamingkeydelivery",KEYFORMATVERSIONS="1"


# Media Playlists
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=133781,BANDWIDTH=134685,CODECS="mp4a.40.2" media-1/stream.m3u8
#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=67526,BANDWIDTH=67854,CODECS="mp4a.40.2" media-2/stream.m3u8

流播放列表如下所示:

代码语言:javascript
复制
#EXTM3U
#EXT-X-VERSION:5
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:30
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://url/to/key?KID=foobar",KEYFORMAT="com.apple.streamingkeydelivery",KEYFORMATVERSIONS="1"
#EXTINF:30.000181,
#EXT-X-BYTERANGE:470290@0
media.aac
# more segments...
#EXT-X-ENDLIST

下载资产:

代码语言:javascript
复制
AVURLAsset *asset = [AVURLAsset assetWithURL:myM3u8Url];
[asset.resourceLoader setDelegate:[FairPlayKeyManager instance] queue:[FairPlayKeyManager queue]];
asset.resourceLoader.preloadsEligibleContentKeys = YES;
AVAssetDownloadTask *task = [self.session assetDownloadTaskWithURLAsset:asset assetTitle:@"Track" assetArtworkData:imgData options:nil];
[task resume];

在委托的URLSession:assetDownloadTask:didFinishDownloadingToURL:

代码语言:javascript
复制
self.downloadedPath = location.relativePath;

在委托的URLSession:task:didCompleteWithError:

代码语言:javascript
复制
if (!error)
{
  NSString *strUrl = [NSHomeDirectory() stringByAppendingPathComponent:self.downloadedPath];
  NSURL *url = [NSURL fileURLWithPath:strUrl];
  AVURLAsset *localAsset = [AVURLAsset assetWithURL:url];
  if (!localAsset.assetCache.playableOffline)
    NSLog(@"Oh no!"); //not playable offline
}

除了无法脱机播放的资产缓存报告之外,下载不会出现错误。但是,如果您切换到飞机模式并尝试播放下载的资产,它将正确地要求资源加载器委托获取一个键(我使用的是持久密钥,所以离线运行良好),然后尝试向media-1/stream.m3u8发出请求。

有什么我没有处理的问题吗?播放列表文件应该在某种程度上有所不同吗?任务或资产上是否有我丢失的财产?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-01-31 21:00:20

事实上,这是因为我从(例如)下载音频的URL。https://mywebsite.com/path/to/master.m3u8重定向到CDN (https://my.cdn/other/path/to/master.m3u8)。AVAssetDownloadTask簿记中出现了一些问题,以至于当我尝试脱机播放下载的文件时,它认为它需要更多的网络文件。我已经把这个归档为雷达43285278了。我通过手动对同一个URL执行HEAD请求来解决这个问题,然后为AVAssetDownloadTask提供最终的重定向URL。

票数 4
EN

Stack Overflow用户

发布于 2019-01-31 05:17:30

在检查asset.assetCache.isPlayableOffline之前,我认为您有一些事情要检查。

  1. 您的KSM配置为支持离线公平游戏吗?
    • 访问苹果FairPlay流媒体网站
    • 下载公平游戏示例SDK (FairPlay流服务器SDK (4.2.0))
    • 打开HLSCatalogWithFPS - AVAssetResourceLoader或HLSCatalogWithFPS - AVContentKeySession
    • 调整您的KSM示例项目,以检查FPS脱机播放是否运行良好。

  1. 检查您的密钥请求过程
    • 由于您没有提供任何与密钥请求过程相关的代码,所以我不知道您是否正确地请求和接收了ckc数据。
    • 完成下载并不意味着您已经获得ckc或持久化密钥。调试以检查是否从KSM获得正确的ckc数据。(如果您的KSM没有将内容配置为脱机可播放的,则请求具有持久键选项的ckc时可能会出错)

代码语言:javascript
复制
func handlePersistableContentKeyRequest(keyRequest: AVPersistableContentKeyRequest) {

        /*
         The key ID is the URI from the EXT-X-KEY tag in the playlist (e.g. "skd://key65") and the
         asset ID in this case is "key65".
         */
        guard let contentKeyIdentifierString = keyRequest.identifier as? String,
            let contentKeyIdentifierURL = URL(string: contentKeyIdentifierString),
            let assetIDString = contentKeyIdentifierURL.host,
            let assetIDData = assetIDString.data(using: .utf8)
            else {
                print("Failed to retrieve the assetID from the keyRequest!")
                return
        }

        do {

            let completionHandler = { [weak self] (spcData: Data?, error: Error?) in
                guard let strongSelf = self else { return }
                if let error = error {
                    keyRequest.processContentKeyResponseError(error)

                    strongSelf.pendingPersistableContentKeyIdentifiers.remove(assetIDString)
                    return
                }

                guard let spcData = spcData else { return }

                do {
                    // Send SPC to Key Server and obtain CKC
                    let ckcData = try strongSelf.requestContentKeyFromKeySecurityModule(spcData: spcData, assetID: assetIDString)

                    let persistentKey = try keyRequest.persistableContentKey(fromKeyVendorResponse: ckcData, options: nil)

                    try strongSelf.writePersistableContentKey(contentKey: persistentKey, withContentKeyIdentifier: assetIDString)

                    /*
                     AVContentKeyResponse is used to represent the data returned from the key server when requesting a key for
                     decrypting content.
                     */
                    let keyResponse = AVContentKeyResponse(fairPlayStreamingKeyResponseData: persistentKey)

                    /*
                     Provide the content key response to make protected content available for processing.
                     */
                    keyRequest.processContentKeyResponse(keyResponse)

                    let assetName = strongSelf.contentKeyToStreamNameMap.removeValue(forKey: assetIDString)!

                    if !strongSelf.contentKeyToStreamNameMap.values.contains(assetName) {
                        NotificationCenter.default.post(name: .DidSaveAllPersistableContentKey,
                                                        object: nil,
                                                        userInfo: ["name": assetName])
                    }

                    strongSelf.pendingPersistableContentKeyIdentifiers.remove(assetIDString)
                } catch {
                    keyRequest.processContentKeyResponseError(error)

                    strongSelf.pendingPersistableContentKeyIdentifiers.remove(assetIDString)
                }
            }

            // Check to see if we can satisfy this key request using a saved persistent key file.
            if persistableContentKeyExistsOnDisk(withContentKeyIdentifier: assetIDString) {

                let urlToPersistableKey = urlForPersistableContentKey(withContentKeyIdentifier: assetIDString)

                guard let contentKey = FileManager.default.contents(atPath: urlToPersistableKey.path) else {
                    // Error Handling.

                    pendingPersistableContentKeyIdentifiers.remove(assetIDString)

                    /*
                     Key requests should never be left dangling.
                     Attempt to create a new persistable key.
                     */
                    let applicationCertificate = try requestApplicationCertificate()
                    keyRequest.makeStreamingContentKeyRequestData(forApp: applicationCertificate,
                                                                  contentIdentifier: assetIDData,
                                                                  options: [AVContentKeyRequestProtocolVersionsKey: [1]],
                                                                  completionHandler: completionHandler)

                    return
                }

                /*
                 Create an AVContentKeyResponse from the persistent key data to use for requesting a key for
                 decrypting content.
                 */
                let keyResponse = AVContentKeyResponse(fairPlayStreamingKeyResponseData: contentKey)

                // Provide the content key response to make protected content available for processing.
                keyRequest.processContentKeyResponse(keyResponse)

                return
            }

            let applicationCertificate = try requestApplicationCertificate()

            keyRequest.makeStreamingContentKeyRequestData(forApp: applicationCertificate,
                                                          contentIdentifier: assetIDData,
                                                          options: [AVContentKeyRequestProtocolVersionsKey: [1]],
                                                          completionHandler: completionHandler)
        } catch {
            print("Failure responding to an AVPersistableContentKeyRequest when attemping to determine if key is already available for use on disk.")
        }
    }
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51548031

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档