You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

tvOS Swift中如何通过AVPlayer选择m3u8流的音轨与字幕

你遇到的返回空问题核心是两个错误实现:

  1. 未异步加载AVAsset的媒体特性属性,直接同步读取未加载完成的属性会返回空值
  2. 监听对象错误,你监听了AVPlayer的status,实际媒体轨道信息挂载在AVPlayerItem下,且部分HLS流的轨道信息会在item进入readyToPlay状态后才完成加载,需要额外监听变更通知。
正确实现步骤

1. 初始化时加载必要属性

创建AVPlayerItem之后,先异步加载asset的相关属性:

let asset = AVURLAsset(url: yourM3U8URL)
// 预加载需要用到的属性
asset.loadValuesAsynchronously(forKeys: ["availableMediaCharacteristicsWithMediaSelectionOptions", "mediaSelectionGroups"]) { [weak self] in
    DispatchQueue.main.async {
        let playerItem = AVPlayerItem(asset: asset)
        // 监听item的status
        playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.new], context: nil)
        // 监听媒体特性变更通知
        NotificationCenter.default.addObserver(self, selector: #selector(self.mediaCharacteristicsChanged), name: .AVPlayerItemAvailableMediaCharacteristicsDidChange, object: playerItem)
        self?.player.replaceCurrentItem(with: playerItem)
    }
}

2. 状态监听处理

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard keyPath == #keyPath(AVPlayerItem.status),
          let item = object as? AVPlayerItem,
          item.status == .readyToPlay else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    player.appliesMediaSelectionCriteriaAutomatically = false
    reloadMediaOptions()
}

@objc private func mediaCharacteristicsChanged() {
    // 媒体特性更新时重新加载可选轨道
    reloadMediaOptions()
}

private func reloadMediaOptions() {
    guard let asset = player.currentItem?.asset else { return }
    // 先检查属性是否加载完成
    var error: NSError?
    let status = asset.statusOfValue(forKey: "availableMediaCharacteristicsWithMediaSelectionOptions", error: &error)
    guard status == .loaded, error == nil else { return }
    
    for characteristic in asset.availableMediaCharacteristicsWithMediaSelectionOptions {
        print("媒体特性: \(characteristic.rawValue)")
        guard let group = asset.mediaSelectionGroup(forMediaCharacteristic: characteristic) else { continue }
        for option in group.options {
            print("  可选轨道: \(option.displayName), 语言: \(option.extendedLanguageTag ?? "无")")
        }
    }
}

3. 切换音轨/字幕的实现

// 切换指定类型的轨道
func selectMediaOption(characteristic: AVMediaCharacteristic, optionDisplayName: String) {
    guard let asset = player.currentItem?.asset,
          let group = asset.mediaSelectionGroup(forMediaCharacteristic: characteristic) else { return }
    // 找到对应名称的option
    guard let targetOption = group.options.first(where: { $0.displayName == optionDisplayName }) else { return }
    // 选中该轨道
    player.currentItem?.select(targetOption, in: group)
}

// 调用示例:切换音频轨道
// selectMediaOption(characteristic: .audible, optionDisplayName: "英语")
// 调用示例:切换字幕
// selectMediaOption(characteristic: .legible, optionDisplayName: "简体中文")

常见排查点

  • 不要使用player.currentItem?.tracks获取可选轨道,该属性仅返回当前已选中的轨道,所有可选轨道必须通过mediaSelectionGroup获取
  • 确认你的HLS流符合规范,多音轨和字幕必须通过EXT-X-MEDIA标签在m3u8主清单中正确声明,未按规范声明的轨道AVFoundation不会识别
  • 如果你需要强制加载所有轨道信息,可以给AVURLAsset添加配置:let config = AVURLAssetConfiguration() config.preloadsAllDefaultMediaSelectionOptions = true,初始化asset时传入配置即可

内容的提问来源于stack exchange,提问作者Сергей Бабнеев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 04:24:06