在 AVFoundation 中探索 HLS 变体
Highlight
iOS 15 的
AVAssetVariantAPI 让应用可以直接从 master playlist 中读取变体属性——码率、分辨率、HDR 类型、音频格式,无需再手动解析 playlist 文本。下载场景下,NSPredicate驱动的AVAssetVariantQualifier让变体选择从”几个固定选项”进化为”任意条件组合”。
核心内容
HLS 的 master playlist 中包含多个变体流(variant),每个变体有不同的分辨率、码率、codec 和 HDR 设置。过去,应用想要展示”4K Dolby Vision”或”Dolby Atmos”这样的 badge,需要手动解析 playlist 文本,容易出错且难以维护。
iOS 15 引入的 AVAssetVariant 将变体信息暴露为结构化 API。AVAssetVariant.VideoAttributes 和 AVAssetVariant.AudioAttributes 分别封装视频和音频属性,应用可以直接读取。下载场景下,AVAssetVariantQualifier 配合 NSPredicate 实现了灵活的变体筛选,支持码率上限、HDR 偏好、无损音频等任意条件组合。
详细内容
变体检查 API
以一个典型的 master playlist 为例(00:40):
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:TYPE=AUDIO,NAME="English",GROUP-ID="stereo",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="en_stereo.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,NAME="French",GROUP-ID="stereo",LANGUAGE="fr",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="fr_stereo.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,NAME="English",GROUP-ID="atmos",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="16/JOC",URI="en_atmos.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,NAME="French",GROUP-ID="atmos",LANGUAGE="fr",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="16/JOC",URI="fr_atmos.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=14516883,VIDEO-RANGE=SDR,CODECS="avc1.64001f,mp4a.40.5",AUDIO="stereo",FRAME-RATE=23.976,RESOLUTION=1920x1080
sdr_variant.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=34516883,VIDEO-RANGE=PQ,CODECS="dvh1.05.06,ec-3",AUDIO="atmos",FRAME-RATE=23.976,RESOLUTION=3840x1920
dovi_variant.m3u8
这个 playlist 有两个变体:SDR + 立体声(1080p),以及 Dolby Vision + Dolby Atmos(4K)。
iOS 15 起可以直接用 API 读取这些属性:
let asset = AVURLAsset(url: masterPlaylistURL)
// 获取所有变体
for variant in asset.variants {
let bitrate = variant.peakBitRate
if let videoAttrs = variant.videoAttributes {
let resolution = videoAttrs.presentationSize // CGSize
let frameRate = videoAttrs.frameRate
let videoRange = videoAttrs.videoRange // .sdr, .hdr, .pq, .hlg
}
if let audioAttrs = variant.audioAttributes {
let channelCount = audioAttrs.channelCount
let formatIDs = audioAttrs.formatIDs
}
}
应用可以基于这些属性在 UI 上展示 badge(如”4K”、“Dolby Vision”、“Dolby Atmos”),无需手动解析 playlist。
下载场景下的变体选择
HLS 离线下载从 2016 年开始支持。iOS 15 之前,变体选择通过有限的 downloadTask options 实现——HDR 开关、无损音频开关等几个固定选项。
iOS 15 引入 AVAssetVariantQualifier,用 NSPredicate 表达变体偏好(03:38):
// 码率上限: prefer 5Mbps 以下的变体
let peakBitRateCap = NSPredicate(format: "peakBitRate < %d", 5_000_000)
let peakBitRateCapQualifier = AVAssetVariantQualifier(predicate: peakBitRateCap)
// HDR 偏好:只选择 PQ(Dolby Vision)变体
let hdrOnlyPredicate = NSPredicate(
format: "videoAttributes.videoRange == %@",
argumentArray: [AVVideoRange.pq]
)
let hdrOnlyQualifier = AVAssetVariantQualifier(predicate: hdrOnlyPredicate)
组合多个条件(04:46):
let compoundPredicate = NSPredicate(
format: "videoAttributes.videoRange == %@ && peakBitRate < %d",
argumentArray: [AVVideoRange.pq, 5_000_000]
)
let variantPref = AVAssetVariantQualifier(predicate: compoundPredicate)
Content Configuration
AVAssetDownloadContentConfiguration 将变体偏好和媒体选择绑定在一起(04:46):
let myMediaSelections: [AVMediaSelection] = [
enAudioMS, // 英语音频
frAudioMS, // 法语音频
enLegibleMS // 英文字幕
]
let contentConfig = AVAssetDownloadContentConfiguration()
contentConfig.variantQualifiers = [variantPref]
contentConfig.mediaSelections = myMediaSelections
Download Configuration
AVAssetDownloadConfiguration 是下载配置的根接口(06:29):
let asset = AVURLAsset(url: masterPlaylistURL)
let dwConfig = AVAssetDownloadConfiguration(asset: asset, title: "my-title")
// Primary content config:HDR + 5Mbps 以下 + 英法音频 + 英文字幕
dwConfig.primaryContentConfiguration.variantQualifiers = [varQf]
dwConfig.primaryContentConfiguration.mediaSelections = [enAudioMS, frAudioMS, enLegibleMS]
// Auxiliary content config:无损英语音频
let auxVarPref = NSPredicate(
format: "%d IN audioAttributes.formatIDs",
argumentArray: [kAudioFormatAppleLossless]
)
let auxVarQf = AVAssetVariantQualifier(predicate: auxVarPref)
let auxContentConfig = AVAssetDownloadContentConfiguration()
auxContentConfig.variantQualifiers = [auxVarQf]
auxContentConfig.mediaSelections = [enAudioMS]
dwConfig.auxiliaryContentConfigurations = [auxContentConfig]
dwConfig.optimizesAuxiliaryContentConfigurations = true
optimizesAuxiliaryContentConfigurations(默认 true)让 AVFoundation 选择无损变体时,确保其视频 rendition 与 primary 相同,避免重复下载视频。
创建下载任务
let session = AVAssetDownloadURLSession(
configuration: .background(withIdentifier: "my-background-session"),
assetDownloadDelegate: myDelegate,
delegateQueue: .main
)
let downloadTask = session.makeAssetDownloadTask(downloadConfiguration: dwConfig)
downloadTask.resume()
// iOS 15 起可以观察下载进度
let progress = downloadTask.progress // KVO-observable
直接指定变体
对于难以用 predicate 表达的业务逻辑,可以直接指定变体(08:10):
let myVariant: AVAssetVariant = ... // 从 asset.variants 中筛选
let variantQf = AVAssetVariantQualifier(variant: myVariant)
dwConfig.primaryContentConfiguration.variantQualifiers = [variantQf]
注意:直接指定的变体必须确保在下载设备上可播放。
核心启发
-
用 AVAssetVariant 替代手动解析 playlist:结构化 API 比文本解析更可靠,且自动适配 Apple 新增的属性字段。
-
用 NSPredicate 组合任意筛选条件:码率上限、HDR 类型、分辨率、音频格式、声道数等属性都可以作为 predicate 条件,组合方式灵活。
-
Primary + Auxiliary 配置优化下载体积:Primary 配置包含完整的视频+音频+字幕,Auxiliary 只补充额外的音频或字幕轨道。开启
optimizesAuxiliaryContentConfigurations避免视频重复下载。 -
利用 NSProgress 展示下载进度:iOS 15 起
AVAssetDownloadTask.progress是 KVO-observable 的,可以直接绑定到 UIProgressView。 -
下载前验证设备兼容性:直接指定变体时,确认目标变体的 codec 和分辨率在下载设备上受支持。HDR 内容在 SDR 设备上无法播放。
关联 Session
- WWDC2021 10140 - HLS 动态广告插播探索 — HLS Interstitials 的变体处理
- WWDC2021 10142 - HLS 无缝媒体过渡 — 多资源连续播放的变体切换
- WWDC2020 10049 - HLS 离线下载 — HLS 下载基础
- WWDC2021 10159 - Core Image Kernel 改进 — HDR 内容处理
评论
GitHub Issues · utterances