What's new in streaming audio for Apple Watch
watchOS 进阶 20m

Apple Watch 流媒体音频新特性

What's new in streaming audio for Apple Watch

2020年6月24日

在 Apple 官方观看视频

一句话判断

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