Highlight
Apple 用
AVAssetDownloadTask下载单个 HLS 媒体选择,用AVAggregateAssetDownloadTask下载多组音频和字幕选择,并通过AVAssetCache、AVContentKeySession和AVAssetDownloadStorageManager处理离线播放、FairPlay 密钥和系统存储管理。
核心内容
很多视频 App 已经用 HLS 做在线播放。用户上飞机前、出门前,或者不想消耗蜂窝流量时,会希望把同一份内容提前下载到设备上。Apple 在这场 Session 里给出的思路很直接:同一套 HLS 资源既可以用于在线播放,也可以通过 AVFoundation 下载后离线播放,受保护内容仍然可以使用 FairPlay Streaming。
离线 HLS 的入口是一个 download task。它运行在 URLSession 体系里,所以任务会按照系统资源和网络状态调度。网络差时不一定立刻开始,遇到超时也会由 AVFoundation 自动重试。App 需要做的,是创建任务、监听进度、保存下载位置,再用下载好的资源创建播放器项目。
下载策略分两种。AVAssetDownloadTask 适合下载一组视频、音频和字幕组合,它会按设备地区和本地化偏好自动选择 media selection。AVAggregateAssetDownloadTask 适合让用户明确选择多组音频和字幕,例如英语音轨、西班牙语音轨和法语字幕一起离线保存。进度统计也随之变复杂,演讲建议把视频、音频、字幕按权重拆开估算,示例里视频约占 70% 的下载时间。
下载完成后,播放不必重新发明一套路径。下载还在进行时,可以复用创建任务时的 AVAsset,让 AVFoundation 在播放和下载之间共享资源。下载完成后,如果原来的 task 对象已经不在内存里,就用保存下来的 file URL 重新创建 AVURLAsset。AVAssetCache 负责回答当前 asset 是否可离线播放,以及哪些 media selection 已经可离线播放。
受保护内容的关键是离线 key。在线播放时,播放器可以按需联系 key server;离线播放时,设备可能没有网络,所以 App 要在下载阶段通过 AVContentKeySession 生成并保存 offline key。key server 可以设置过期时间。用户删除下载时,App 还应该主动 invalidate 对应的 persistable content key。
iOS 14 的更新集中在体验和治理上。下载质量可以按最低码率、最小展示尺寸、HDR 偏好和多声道音频偏好控制。存储管理可以交给 AVAssetDownloadStorageManager,让系统按过期时间和优先级清理离线媒体,也允许用户在系统设置里删除这些内容。
详细内容
创建单组 HLS 下载任务
(02:52)AVAssetDownloadTask 下载一组视频、音频和字幕组合。创建任务前,先用 HLS URL 创建 AVURLAsset,再用 background URLSessionConfiguration 创建 AVAssetDownloadURLSession。示例里通过 AVAssetDownloadTaskMinimumRequiredMediaBitrateKey 请求 2 Mbps 的下载版本。
let hlsAsset = AVURLAsset(url: assetURL)
let backgroundConfiguration = URLSessionConfiguration.background(
withIdentifier: "assetDownloadConfigurationIdentifier")
let assetURLSession = AVAssetDownloadURLSession(configuration: backgroundConfiguration,
assetDownloadDelegate: self, delegateQueue: OperationQueue.main())
// Download a Movie at 2 mbps
let assetDownloadTask = assetURLSession.makeAssetDownloadTask(asset: hlsAsset,
assetTitle: "My Movie", assetArtworkData: myAssetArtwork,
options: [AVAssetDownloadTaskMinimumRequiredMediaBitrateKey: 2000000])!
assetDownloadTask.resume()
// AVAssetDownloadTask uses automatic media selection
关键点:
AVURLAsset(url:)指向原始 HLS master playlist,下载和播放都围绕这个 asset 展开。- background configuration 让下载任务可以在 App 进入后台后继续运行。
AVAssetDownloadURLSession使用AVAssetDownloadDelegate回调下载状态。makeAssetDownloadTask创建单组 media selection 的下载任务。AVAssetDownloadTaskMinimumRequiredMediaBitrateKey用来限制下载版本的最低码率。AVAssetDownloadTask使用 automatic media selection,会根据地区和本地化偏好选择音频和字幕。
监听单个下载任务进度
(03:41)下载进度回调用媒体时间范围表达,不使用字节数。这样进度更接近播放器界面里的时长概念。完成状态则通过 didCompleteWithError 通知。
// Monitor AVAssetDownloadTask
public protocol AVAssetDownloadDelegate: URLSessionTaskDelegate {
// Use to monitor progress
func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask,
didLoad timeRange: CMTimeRange, totalTimeRangesLoaded loadedTimeRanges: [NSValue],
timeRangeExpectedToLoad: CMTimeRange)
// Listen for completion
func urlSession(_ session: URLSession, task: URLSessionTask,
didCompleteWithError error: Error?)
}
关键点:
didLoad timeRange回调本次加载的媒体时间范围。loadedTimeRanges汇总已经下载完成的时间范围。timeRangeExpectedToLoad是预计要加载的总时间范围,适合计算百分比。didCompleteWithError既表示成功完成,也承载下载失败的错误信息。
让用户选择多组音频和字幕
(04:55)如果用户需要下载多语言音轨或字幕,先从 asset 的 media selection group 中挑选目标 option。演讲示例把每个选择写入 myMediaSelections,再交给 aggregate download task。
let hlsAsset = AVURLAsset(url: assetURL)
let myMediaSelections = [] // audio media-selections followed by subtitle media-selections
guard hlsAsset.statusOfValue(forKey: "availableMediaCharacteristicsWithMediaSelectionOptions", error: nil)
== AVKeyValueStatus.loaded else { return }
let mediaCharacteristic = //AVMediaCharacteristic.audible or AVMediaCharacteristic.legible
let mediaSelectionGroup = hlsAsset.mediaSelectionGroup(forMediaCharacteristic: mediaCharacteristic)
if let options = mediaSelectionGroup?.options {
for option in options {
// chose your media selection option
if /* this is my option */ {
let mutableMediaSelection = hlsAsset.preferredMediaSelection.mutableCopy()
mutableMediaSelection.select(option, in: mediaSelectionGroup)
myMediaSelections.append(mutableMediaSelection)
}
}
}
关键点:
availableMediaCharacteristicsWithMediaSelectionOptions必须先加载,才能枚举可选音频和字幕。AVMediaCharacteristic.audible对应音频,AVMediaCharacteristic.legible对应字幕。mediaSelectionGroup(forMediaCharacteristic:)取出指定类型的可选项集合。mutableMediaSelection.select把用户选择的 option 写入一个 media selection。myMediaSelections可以按音频、字幕顺序收集多组下载目标。
创建聚合下载任务
(05:11)AVAggregateAssetDownloadTask 用于下载多组 media selection。它仍然使用 AVAssetDownloadURLSession,差别在于创建任务时传入 media selections 数组。
let hlsAsset = AVURLAsset(url: assetURL)
let myMediaSelections = ...
let backgroundConfiguration = URLSessionConfiguration.background(
withIdentifier: "assetDownloadConfigurationIdentifier")
let assetURLSession = AVAssetDownloadURLSession(configuration: backgroundConfiguration,
assetDownloadDelegate: self, delegateQueue: OperationQueue.main())
// Download a Movie at 2 mbps
let aggDownloadTask = assetURLSession.aggregateAssetDownloadTask(with: hlsAsset,
mediaSelections: myMediaSelections,
assetTitle: "My Movie",
assetArtworkData: myAssetArtwork,
options:[AVAssetDownloadTaskMinimumRequiredMediaBitrateKey: 2000000])!
aggDownloadTask.resume()
关键点:
aggregateAssetDownloadTask绑定同一个 HLS asset 和多组 media selection。assetTitle与assetArtworkData会在系统管理离线内容时展示给用户。- 码率选项仍然通过
AVAssetDownloadTaskMinimumRequiredMediaBitrateKey传入。 - 对多音轨、多字幕下载,进度 UI 需要按 media selection 分开估算。
恢复后台下载任务
(07:04)下载任务在后台继续运行。App 重新启动时,要用相同的 background configuration identifier 创建 session,再通过 getAllTasks 找回现有任务。
// Restore Tasks on App Launch
class MyAppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let configuration = URLSessionConfiguration.background(withIdentifier:
"assetDownloadConfigurationIdentifier")
let session = URLSession(configuration: configuration)
session.getAllTasks { tasks in
for task in tasks {
if let assetDownloadTask = task as? AVAssetDownloadTask {
// restore progress indicators, state, etc...
}
}
}
}
}
关键点:
- configuration identifier 必须与创建下载任务时相同。
getAllTasks会返回当前 session 下的任务。- 找回
AVAssetDownloadTask后,可以恢复进度条、状态和任务管理界面。 - transcript 还提到可以从原 download task 取回原始
AVAsset继续使用。
判断哪些内容可离线播放
(09:16)下载完成后,AVAssetCache 告诉 App 当前 asset 是否可以离线播放,以及哪些 media selection 已经在本地可用。
// What can I play offline?
public class AVURLAsset {
public var assetCache: AVAssetCache? { get }
}
public class AVAssetCache {
public var isPlayableOffline: Bool { get }
public func mediaSelectionOptions(in mediaSelectionGroup: AVMediaSelectionGroup)
-> [AVMediaSelectionOption]
}
关键点:
AVURLAsset.assetCache是离线可播放状态的入口。isPlayableOffline回答整个 asset 是否能离线播放。mediaSelectionOptions(in:)返回某个 media selection group 中已经可离线播放的选项。- 这适合驱动“已下载音轨”“已下载字幕”等界面状态。
失效离线 FairPlay 密钥
(11:33)离线 HLS 可以继续使用 FairPlay Streaming。App 在下载阶段创建 offline key 并保存,离线播放时用它响应 key request。用户删除下载或需要提前收回授权时,用 AVContentKeySession 失效对应 key。
// Invalidate Offline Key
public class AVContentKeySession {
func invalidatePersistableContentKey(_ persistableContentKeyData: Data,
options: [AVContentKeySessionServerPlaybackContextOption : Any]? = nil,
completionHandler handler: @escaping (Data?, Error?) -> Void)
func invalidateAllPersistableContentKeys(forApp appIdentifier: Data,
options: [AVContentKeySessionServerPlaybackContextOption : Any]? = nil,
completionHandler handler: @escaping (Data?, Error?) -> Void)
}
关键点:
invalidatePersistableContentKey用于失效单个已保存的 offline key。invalidateAllPersistableContentKeys可以按 FairPlay streaming application certificate 对应的 app 标识清理全部 key。- transcript 说明 key server 可以设置 offline key 过期时间。
- 如果 key 在一次已开始的播放期间过期,当前播放会继续到结束,避免中途突然停止。
控制离线下载质量
(13:54)下载越快,质量通常越低;质量越高,占用时间和空间越多。Session 建议给用户提供快速下载和最佳质量两种选择,再用下载任务选项约束具体版本。iOS 14 增加了展示尺寸和 HDR 偏好控制。
// Quality Selection
public class AVAssetDownloadTask {
public let AVAssetDownloadTaskMinimumRequiredMediaBitrateKey: String
//Starting in iOS 14
public let AVAssetDownloadTaskMinimumRequiredPresentationSizeKey: String
public let AVAssetDownloadTaskPrefersHDRKey: String
}
关键点:
AVAssetDownloadTaskMinimumRequiredMediaBitrateKey用最低码率筛选下载版本。AVAssetDownloadTaskMinimumRequiredPresentationSizeKey从 iOS 14 开始可用,用展示尺寸约束质量。AVAssetDownloadTaskPrefersHDRKey控制是否偏好 HDR presentation。- transcript 说明 download task 默认会偏好下载可用的 HDR presentation。
把离线内容交给系统管理
(15:51)Session 建议让操作系统管理离线下载。系统可以在存储紧张或软件更新时回收空间,用户也可以在系统设置里删除媒体。App 通过 storage manager 设置过期时间和优先级。
// AVAssetDownloadStorageManager
// Get the singleton
let storageManager = AVAssetDownloadStorageManager.shared()
// Create the policy
let newPolicy = AVMutableAssetDownloadStorageManagementPolicy()
newPolicy.expirationDate = myExpiryDate
newPolicy.priority = .important
// Set the policy
storageManager.setStorageManagementPolicy(newPolicy, forURL: myDownloadStorageURL)
关键点:
AVAssetDownloadStorageManager.shared()取得系统提供的下载存储管理器。AVMutableAssetDownloadStorageManagementPolicy描述这份内容的清理策略。expirationDate先参与清理判断,priority再决定保留优先级。setStorageManagementPolicy把策略应用到下载内容的系统位置。- transcript 特别提醒,要把下载内容留在系统提供的位置,并准备好处理系统删除后的状态。
核心启发
-
做离线下载前的版本选择页
- 做什么:在视频详情页提供“快速下载”和“最佳质量”两个入口,并显示预计音轨、字幕和画质选择。
- 为什么值得做:Session 明确建议在下载时间和质量之间给用户选择空间,下载任务也提供码率、展示尺寸、HDR 和多声道偏好。
- 怎么开始:用
AVAssetDownloadTaskMinimumRequiredMediaBitrateKey做快速版本,用AVAssetDownloadTaskMinimumRequiredPresentationSizeKey和AVAssetDownloadTaskPrefersHDRKey做高质量版本。
-
做多语言离线包
- 做什么:让用户在下载前选择多个音轨和字幕,例如英文音频、西班牙语音频和法语字幕。
- 为什么值得做:
AVAggregateAssetDownloadTask可以一次下载多组 media selection,解决单任务自动选择不够明确的问题。 - 怎么开始:读取
AVMediaCharacteristic.audible和AVMediaCharacteristic.legible的 media selection group,把用户选项组装进mediaSelections。
-
做下载恢复中心
- 做什么:App 启动后恢复所有后台 HLS 下载任务,继续显示进度、状态和错误。
- 为什么值得做:下载任务会在后台继续运行,重新启动时需要用同一个 session identifier 找回任务。
- 怎么开始:在 app launch 创建相同 identifier 的 background session,调用
getAllTasks,为AVAssetDownloadTask恢复 UI 状态。
-
做离线授权到期提醒
- 做什么:在离线视频列表里显示 FairPlay offline key 的到期状态,并在联网时提前刷新即将过期的 key。
- 为什么值得做:transcript 说明 key server 可以设置 offline key 过期时间,App 需要在过期前创建新的 offline key。
- 怎么开始:保存 offline key 时记录授权有效期,App 回到前台或进入下载页时扫描即将过期的项目。
-
做系统存储策略面板
- 做什么:给离线视频设置过期日期和重要程度,并提示用户系统可能在空间不足时清理内容。
- 为什么值得做:
AVAssetDownloadStorageManager允许系统按过期时间和优先级回收离线媒体,还能让用户在设置里删除内容。 - 怎么开始:为每个下载 URL 设置
AVMutableAssetDownloadStorageManagementPolicy,并在播放前用AVAssetCache.isPlayableOffline重新确认本地状态。
关联 Session
- Author fragmented MPEG-4 content with AVAssetWriter — 学习如何生成适合 HLS 输出的 fragmented MPEG-4 内容。
- Deliver a better HLS audio experience — 继续了解 HLS 音频编码、带宽受限网络和多声道交付。
- Improve stream authoring with HLS Tools — 用 HLS 工具检查直播和点播流的 authoring 质量。
- What’s new in Low-Latency HLS — 理解同年 HLS 协议更新和低延迟播放链路。
- Adapt ad insertion to Low-Latency HLS — 了解低延迟 HLS 中广告插入和内容切换的处理方式。
评论
GitHub Issues · utterances