tvOS Swift中如何通过AVPlayer选择m3u8流的音轨与字幕
你遇到的返回空问题核心是两个错误实现:
- 未异步加载AVAsset的媒体特性属性,直接同步读取未加载完成的属性会返回空值
- 监听对象错误,你监听了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,提问作者Сергей Бабнеев
相关产品推荐
相关产品推荐

