打造更好的 HLS 音频体验
Deliver a better HLS audio experience
2020年6月25日
一句话判断
如果你在做播客、音乐或任何音频流媒体应用,HLS 的新音频能力——特别是无损和空间音频支持——会直接决定你能不能搭上 Apple Music 带起的高品质音频浪潮。
这场 Session 讲了什么
HTTP Live Streaming (HLS) 在 2020 年迎来了一系列针对音频体验的重要更新。Session 首先回顾了 HLS 音频的基础架构——通过 EXT-X-MEDIA 标签定义音频 Rendition,支持多码率切换和多语言音轨。然后重点介绍了今年的新特性:对 ALAC(Apple Lossless Audio Codec)无损音频的支持,以及对空间音频(Spatial Audio)的传输优化。
无损音频意味着你可以通过 HLS 传输采样率高达 192kHz、位深 24-bit 的音频流。这在以前的 HLS 音频场景中几乎不可想象,因为传统上 HLS 音频主要使用 AAC 编码。Session 详细讨论了如何在 playlist 中声明无损音频 Rendition,以及客户端如何根据网络条件和设备能力选择合适的音频质量。
空间音频部分介绍了如何通过 HLS 传递 Dolby Atmos 内容。这需要在 playlist 中正确配置 CHANNELS 属性和 AUDIO 标签组合,确保支持 Atmos 的设备(如 AirPods Pro、AirPods Max)能够正确解码和渲染沉浸式音频。
值得深挖的点
无损音频的码率策略
ALAC 的码率远高于 AAC——一首 4 分钟的无损歌曲可能需要 30-50MB 数据量,而 AAC 版本只需要 8-10MB。这意味着你需要为无损音频提供合理的缓冲策略和码率切换机制。Session 建议同时提供 AAC(低码率)和 ALAC(高码率)两种 Rendition,让客户端根据网络条件自动选择。在网络不佳时回退到 AAC,网络良好时切换到无损。
HLS Playlist 中的音频配置细节
配置多 Rendition 音频需要在 Master Playlist 中正确设置 CODECS、CHANNELS 和 SAMPLE-RATE 属性。无损音频需要声明 alac 编解码器,空间音频需要指定通道布局(如 6 表示 5.1 布局)。这些属性如果不一致,AVPlayer 可能无法正确选择和切换 Rendition。
代码片段
# HLS Master Playlist 配置无损和 AAC 音频
#EXTM3U
# AAC 音频 Rendition(标准品质)
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="标准",
LANGUAGE="zh-CN",DEFAULT=YES,AUTOSELECT=YES,
URI="audio/aac/main.m3u8"
# ALAC 无损音频 Rendition(无损品质)
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="lossless",NAME="无损",
LANGUAGE="zh-CN",DEFAULT=NO,AUTOSELECT=NO,
URI="audio/alac/main.m3u8"
# Dolby Atmos 空间音频
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="atmos",NAME="空间音频",
LANGUAGE="zh-CN",DEFAULT=NO,AUTOSELECT=NO,
CHANNELS="6",URI="audio/atmos/main.m3u8"
# 视频流配置,关联音频组
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,
AUDIO="aac",CODECS="avc1.640028,mp4a.40.2"
video/1080p/main.m3u8
# 无损版本的视频流
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,
AUDIO="lossless",CODECS="avc1.640028,alac"
video/1080p/main.m3u8
import AVFoundation
// 配置 AVPlayer 支持高品质音频
func setupAudioPlayer(url: URL) -> AVPlayer {
let asset = AVAsset(url: url)
// 请求无损音频的访问权限
let audioSession = AVAudioSession.sharedInstance()
try? audioSession.setCategory(.playback, mode: .default,
options: .allowAirPlay)
try? audioSession.setActive(true)
// 创建 player item 并监听音频 Rendition 切换
let playerItem = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: playerItem)
// 监听当前音频轨道信息
playerItem.audioMix = nil // 使用默认混音
return player
}
// 监听和切换音频品质
import AVFoundation
class AudioQualityManager {
let player: AVPlayer
init(player: AVPlayer) {
self.player = player
}
// 获取可用的音频 Rendition 列表
func availableAudioRenditions() -> [AVAssetMediaSelectionOption] {
guard let currentItem = player.currentItem else { return [] }
let asset = currentItem.asset
let group = asset.mediaSelectionGroup(forMediaCharacteristic: .audible)
return group?.options ?? []
}
// 选择无损音频 Rendition
func selectLosslessAudio() {
guard let currentItem = player.currentItem,
let group = currentItem.asset.mediaSelectionGroup(
forMediaCharacteristic: .audible) else { return }
// 找到无损音频选项
let options = group.options
for option in options {
if let name = option.displayName as? String,
name.contains("无损") {
currentItem.select(option, in: group)
break
}
}
}
}
最佳实践
- 为无损音频准备至少两倍于 AAC 的缓冲区大小,避免频繁卡顿
- 在 UI 中明确告知用户当前播放品质(标准/无损/空间音频),让用户知道自己在消耗额外的带宽
- 使用
AVAssetMediaSelectionGroup的options属性动态检测可用的音频 Rendition,不要硬编码 - 无损音频文件体积大,建议只在 Wi-Fi 环境下默认开启,蜂窝网络需要用户手动切换
- 测试时用 Network Link Conditioner 模拟不同网络环境,验证 Rendition 切换是否平滑
还有什么值得关注
- Session 提到 iOS 14 的 AVFoundation 增强了对
CHANNELS属性的解析能力,之前某些多声道配置可能被忽略 - 空间音频需要用户佩戴支持的设备(如 AirPods Pro/Max)才能体验,应用层应该检测设备能力再决定是否展示相关 UI
- 新的 HLS Interstitials 功能可以在音频流中插入广告或赞助内容,对播客类应用很有用