使用AVAssetDownloadTask进行HLS缓存

问题描述

我正在关注Apple关于缓存HLS(.m3u8)视频的文档。

https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/MediaPlaybackGuide/Contents/Resources/en.lproj/HTTPLiveStreaming/HTTPLiveStreaming.html

在文档的播放脱机资产下,指示使用 avassetdownloadtask的资产同时开始播放。

func downloadAndplayAsset(_ asset: AVURLAsset) {
    // Create new avassetdownloadtask for the desired asset
    // Passing a nil options value indicates the highest available bitrate should be downloaded
    let downloadTask = downloadSession.makeAssetDownloadTask(asset: asset,assetTitle: assetTitle,assetArtworkData: nil,options: nil)!
    // Start task
    downloadTask.resume()

    // Create standard playback items and begin playback
    let playerItem = AVPlayerItem(asset: downloadTask.urlAsset)
    player = AVPlayer(playerItem: playerItem)
    player.play()
}

问题是同一资产被下载了两次

在初始化AVPlayer之后,它立即开始缓冲资产。最初,我假设必须使用缓冲区中的数据来创建缓存,但是 avassetdownloadtask 不会开始下载数据进行缓存,直到AVPlayer完成播放资产为止。缓冲的数据基本上被丢弃。

我在 currentItem.loadedTimeRanges 上使用了KVO来检查缓冲区的状态。

playerTimeRangesObserver = currentPlayer.observe(\.currentItem?.loadedTimeRanges,options: [.new,.old]) { (player,change) in

    let time = self.currentPlayer.currentItem?.loadedTimeRanges.firs.                                                
    if let t = time {
         print(t.timeRangeValue.duration.seconds)
    }
}

以下检查avassetdownloadtask下载状态的方法

/// Method to adopt to subscribe to progress updates of an avassetdownloadtask.
func urlSession(_ session: URLSession,assetDownloadTask: avassetdownloadtask,didLoad timeRange: CMTimeRange,totalTimeRangesLoaded loadedTimeRanges: [NSValue],timeRangeExpectedToLoad: CMTimeRange) {

    // This delegate callback should be used to provide download progress for your avassetdownloadtask.
    guard let asset = activeDownloadsMap[assetDownloadTask] else { return }

    var percentComplete = 0.0
    for value in loadedTimeRanges {
        let loadedTimeRange: CMTimeRange = value.timeRangeValue
        percentComplete +=
            loadedTimeRange.duration.seconds / timeRangeExpectedToLoad.duration.seconds
    }
    
    print("PercentComplete for \(asset.stream.name) = \(percentComplete)")
}

这是正确的行为还是我做错了什么? 我希望能够使用正在缓存的视频数据(正在下载avassetdownloadtask)在AVPlayer中播放。

解决方法

您的 AVAssetDownloadTask 必须配置为下载与您的 AVPlayerItem 请求不同的 HLS 变体。

如果您已经通过 AVAssetDownloadTask 下载了一些数据,您的 AVPlayerItem 将随后使用它。 但是如果你已经有一些 AVPlayerItem 下载的数据,你的 AVAssetDownloadTask 可能会忽略它,因为它需要满足你下载配置的要求。