Explore HLS variants in AVFoundation
Audio & Video 进阶 24m

探索 AVFoundation 中的 HLS 变体

Explore HLS variants in AVFoundation

2021年6月11日

在 Apple 官方观看视频

一句话判断

如果你只用 AVPlayer 播放 HLS 却从没碰过 AVAssetVariant 和 AVVariantPreferences,你大概率没有充分利用 Apple 为自适应流媒体准备的精细化控制能力。

这场 Session 讲了什么

Session 详细介绍了 AVFoundation 框架中与 HLS variant(变体流)相关的 API,包括如何获取和解析 master playlist 中的 variant 信息、如何自定义 variant 选择策略、以及如何监控播放过程中的 variant 切换事件。

HLS 的自适应码率(ABR)逻辑在 AVFoundation 中是自动的——播放器根据网络状况自动选择合适的分辨率和码率。但”自动”不等于”黑盒”。iOS 15 新增了 AVVariantPreferences API,让你可以在播放器自动选择的基础上施加额外约束:比如在蜂窝网络下限制最高 720p,或者强制选择包含特定音频语言的 variant。Session 还展示了 AVAssetVariant 的属性结构——你可以读取每个 variant 的码率、分辨率、codec、HDR 信息,甚至视频范围(Video Range)标签。

值得深挖的点

AVVariantAttributes 的查询能力

AVAssetVariant 包含一组 AVVariantAttributes,描述了这个 variant 的完整能力参数。以前开发者只能拿到码率和分辨率,现在可以读取 codec 类型(H.264、HEVC、VP9)、颜色空间(BT.709、BT.2020)、传输特性(SDR、HLG、PQ)、音频通道数和语言标签。这意味着你可以在播放前就做精确的 variant 筛选——比如只允许 HEVC 的 variant 被选中,因为你知道用户设备支持硬件解码。

自定义 variant 选择策略的实现方式

AVVariantPreferences 通过 preferredForwardBufferDurationpreferredPeakBitRate 等属性控制播放器的行为。但更强大的用法是配合 AVPlayerItemVariant 的 KVO 监听,在 variant 切换发生时做自定义处理。比如检测到用户从 WiFi 切到蜂窝网络后,立刻降低 preferredPeakBitRate,而不是等播放器自动降级(自动降级通常会有几秒的缓冲期)。

代码片段

// 读取 HLS asset 的所有 variant 信息
func analyzeVariants(for url: URL) async {
    let asset = AVURLAsset(url: url)
    await asset.loadValues(forKeys: ["variants"])
    
    guard let variants = asset.variants else { return }
    
    for (index, variant) in variants.enumerated() {
        let videoAttributes = variant.videoAttributes ?? []
        for attr in videoAttributes {
            print("Variant \(index): \(attr.pixelWidth)x\(attr.pixelHeight)")
            print("  码率: \(attr.bitRate ?? 0) bps")
            print("  Codec: \(attr.codecString ?? "unknown")")
        }
        
        let audioAttributes = variant.audioAttributes ?? []
        for attr in audioAttributes {
            print("  音频语言: \(attr.languageCode ?? "und")")
            print("  通道数: \(attr.channelCount)")
        }
    }
}
// 使用 AVVariantPreferences 自定义选择策略
func configurePlayer(with url: URL) -> AVPlayer {
    let asset = AVURLAsset(url: url)
    let item = AVPlayerItem(asset: asset)
    
    // 设置偏好:蜂窝网络下限制最高码率
    let preferences = AVVariantPreferences()
    preferences.preferredPeakBitRate = 2_500_000  // 2.5 Mbps
    
    // 设置最大前向缓冲时长
    preferences.preferredForwardBufferDuration = 10.0
    
    item.preferredVariantPreferences = preferences
    
    // 监听当前播放的 variant 变化
    item.addObserver(self, forKeyPath: "currentItem.variantAttributes", 
                     options: .new, context: nil)
    
    return AVPlayer(playerItem: item)
}
// 监听 variant 切换事件
override func observeValue(forKeyPath keyPath: String?, 
                           of object: Any?, 
                           change: [NSKeyValueChangeKey: Any]?, 
                           context: UnsafeMutableRawPointer?) {
    guard keyPath == "currentItem.variantAttributes" else { return }
    
    if let playerItem = object as? AVPlayerItem,
       let attributes = playerItem.variantAttributes {
        let width = attributes.videoAttributes?.first?.pixelWidth ?? 0
        let height = attributes.videoAttributes?.first?.pixelHeight ?? 0
        print("当前播放分辨率: \(width)x\(height)")
        
        // 记录分辨率变化用于 QoE 分析
        AnalyticsService.logResolutionChange(to: "\(width)x\(height)")
    }
}

最佳实践

  • 在播放前分析 variant 列表,如果发现没有任何 1080p 以上的 variant,提前在 UI 上标注”高清不可用”,而不是等用户投诉画质差。
  • 蜂窝网络场景下主动设置 preferredPeakBitRate,不要完全依赖播放器的自动 ABR——自动逻辑的响应速度通常比手动设置慢 3-5 秒。
  • 将 variant 切换事件上报到你的 QoE(Quality of Experience)监控平台,分辨率频繁波动说明网络质量不稳定,可以用来指导 CDN 调度策略。

还有什么值得关注

  • AVAssetVariant 现在可以识别 Dolby Atmos 和 Dolby Vision 标记,你可以根据设备能力决定是否允许这些 variant 被选中。
  • iOS 15 的 AVPlayer 在 variant 切换时增加了对 VIDEO-RANGE 标签的处理,SDR 和 HDR 之间的自动切换更加平滑。
  • AVVariantPreferences 可以和 Network Framework 的 NWPathMonitor 配合,在网络类型变化时动态调整偏好。
WWDC 2021