watchOS 进阶 20m
Apple Watch 流媒体音频新特性
What's new in streaming audio for Apple Watch
2020年6月24日
一句话判断
Apple Watch 终于可以独立播放流媒体音频了——不需要 iPhone 在旁边,直接通过 Wi-Fi 或蜂窝网络播放。
这场 Session 讲了什么
这场 Session 介绍了 watchOS 7 中 Apple Watch 音频流媒体能力的重大更新。在此之前的 watchOS 版本中,Apple Watch 的音频播放主要依赖两种方式:从 iPhone 同步的本地音乐文件,或者通过蓝牙耳机连接 iPhone 播放。watchOS 7 让 Apple Watch 成为独立的流媒体音频终端。
演讲者首先介绍了 AVFoundation 在 watchOS 上的新能力。AVPlayer 现在可以在 Apple Watch 上直接播放网络流媒体音频,支持 HLS 和渐进式下载。这意味着你可以构建一个完整的音乐播放 App,完全运行在手表上,不依赖 iPhone。
Session 的中间部分讨论了 watchOS 音频播放的架构考量。Apple Watch 的内存和 CPU 资源非常有限,音频流解码需要尽量使用硬件解码器。演讲者建议使用 AAC 格式(硬件解码支持),避免使用 FLAC 等需要软件解码的格式。
最后,演讲者介绍了 Now Playing Center 的更新——watchOS 7 中的应用可以通过 MPNowPlayingSession 向系统报告当前播放状态,这样用户可以在表盘的 Complication 中看到播放信息,也可以通过控制中心控制播放。
值得深挖的点
- 后台音频播放的生命周期:watchOS 对后台执行有严格限制,但音频播放有特殊豁免。当你的 App 在后台播放音频时,系统会保持它的运行状态。但如果你暂停播放超过一定时间,系统可能会挂起你的 App。你需要正确处理
WKApplication.isApplicationRunningInDock状态来管理播放生命周期。 - 网络可用性的处理:Apple Watch 可能在 Wi-Fi 和蜂窝网络之间切换,甚至完全断网。你的流媒体 App 需要优雅处理网络中断——暂停播放、缓存当前位置、网络恢复后自动续播。
代码片段
// 在 Apple Watch 上播放流媒体音频
import AVFoundation
class WatchAudioPlayer: ObservableObject {
var player: AVPlayer?
func playStream(url: URL) {
let playerItem = AVPlayerItem(url: url)
player = AVPlayer(playerItem: playerItem)
// 配置音频会话
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("音频会话配置失败: \(error)")
}
player?.play()
}
func stop() {
player?.pause()
player = nil
}
}
// 配置 Now Playing 信息
import MediaPlayer
func updateNowPlayingInfo(title: String, artist: String) {
let center = MPNowPlayingInfoCenter.default()
var info = [String: Any]()
info[MPMediaItemPropertyTitle] = title
info[MPMediaItemPropertyArtist] = artist
info[MPNowPlayingInfoPropertyIsLiveStream] = false
center.nowPlayingInfo = info
}
// 设置远程命令控制
func setupRemoteCommands() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.addTarget { _ in
self.player?.play()
return .success
}
commandCenter.pauseCommand.addTarget { _ in
self.player?.pause()
return .success
}
}
// 处理网络中断和恢复
func observePlayerItem(_ item: AVPlayerItem) {
// 监听网络中断
item.addObserver(
self, forKeyPath: "playbackLikelyToKeepUp",
options: .new, context: nil
)
NotificationCenter.default.addObserver(
forName: .AVPlayerItemPlaybackStalled,
object: item, queue: .main
) { _ in
// 播放缓冲不足,显示加载指示器
self.showBufferingIndicator()
}
NotificationCenter.default.addObserver(
forName: .AVPlayerItemNewErrorLogEntry,
object: item, queue: .main
) { notification in
// 记录播放错误日志
if let log = (notification.object as? AVPlayerItem)?.errorLog() {
for event in log.events {
print("播放错误: \(event.errorStatusCode) - \(event.errorComment ?? "")")
}
}
}
}
最佳实践
- 使用 AAC 编码(64-128kbps)而非无损格式,减少 CPU 和内存消耗
- 实现网络中断的自动恢复,watchOS 的网络不如 iOS 稳定
- 正确设置 Now Playing Info,让用户在表盘和控制中心都能控制播放
- 音频缓冲区设置较小的预加载量(建议 2-3 秒),避免占用过多内存
- 在 watchOS Complication 中显示当前播放状态,提供快捷控制入口
还有什么值得关注
- watchOS 7 还新增了对音频录制功能的支持,可以构建完整的音频创作工具
- 如果你在做播客 App,watchOS 的独立播放能力让你的用户可以不带手机出门跑步听播客
- 搭配 “Record stereo audio with AVAudioSession” 一起看,了解 Apple 平台音频 API 的整体更新
WWDC 2020