求助:AVPlayer无法播放部分.m3u8文件的问题排查及解决方案建议
解决AVPlayer播放部分.m3u8文件崩溃(Error -11819)的方案
首先,Error Domain=AVFoundationErrorDomain Code=-11819 这个错误在iOS 14+系统中,大多和HLS Manifest(.m3u8文件)不符合规范有关——苹果从iOS14开始对HLS的解析规则变得更严格,尤其是对多轨道音频的配置要求。结合你提供的Manifest示例,我梳理出几个核心问题和对应的解决方法:
1. 修复Manifest的音频轨道默认配置
你的Manifest中所有EXT-X-MEDIA标签的DEFAULT和AUTOSELECT都设为NO,但iOS14+要求HLS流必须有至少一个默认可选的音频轨道,否则AVPlayer无法确定要加载哪个轨道,直接触发解析失败。
修改示例:
#EXTM3U #EXT-X-MEDIA:TYPE=AUDIO,URI="_64/index.m3u8",GROUP-ID="2@48000-64000",NAME="AAC 64",DEFAULT=NO,AUTOSELECT=NO #EXT-X-MEDIA:TYPE=AUDIO,URI="_80/index.m3u8",GROUP-ID="2@48000-80000",NAME="AAC 80",DEFAULT=NO,AUTOSELECT=NO #EXT-X-MEDIA:TYPE=AUDIO,URI="_96/index.m3u8",GROUP-ID="2@48000-96000",NAME="AAC 96",DEFAULT=YES,AUTOSELECT=YES #EXT-X-STREAM-INF:BANDWIDTH=133336,CODECS="mp4a.40.2",AUDIO="2@48000-96000" _96/index.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=100641,CODECS="mp4a.40.2",AUDIO="2@48000-64000" _64/index.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=116989,CODECS="mp4a.40.2",AUDIO="2@48000-80000" _80/index.m3u8
把最高码率的音频轨道设为默认,这样AVPlayer能明确加载目标轨道。
2. 验证子Manifest的可访问性
主Manifest中引用的_64/index.m3u8、_80/index.m3u8等子Manifest,需要确保它们能被设备正常访问:
- 检查子Manifest的URL路径是否正确(相对路径是否基于主Manifest的URL)
- 确认后端没有对这些子文件设置访问限制(比如防盗链、权限验证)
- 可以用Safari直接加载子Manifest的完整URL,看是否能正常返回内容
3. 适配iOS14+的HLS规范细节
除了默认轨道,还有几个规范点需要检查:
GROUP-ID的命名尽量避免特殊字符(比如你的2@48000-96000中的@),换成更简洁的命名(如audio_96k),避免解析器识别异常- 确保
EXT-X-STREAM-INF中的AUDIO属性和对应EXT-X-MEDIA的GROUP-ID完全匹配,大小写、字符都不能错 - 检查子Manifest是否符合HLS规范(比如包含正确的
EXT-X-TARGETDURATION、EXT-X-VERSION等标签)
4. 客户端侧的临时兼容方案(如果无法修改后端Manifest)
如果后端暂时无法调整Manifest,你可以在客户端手动修改内容后再传给AVPlayer:
- 先下载主Manifest的字符串内容
- 替换其中一个
EXT-X-MEDIA标签的DEFAULT=NO,AUTOSELECT=NO为DEFAULT=YES,AUTOSELECT=YES - 将修改后的字符串转为本地Data,生成
NSURL(用fileURLWithPath或者dataRepresentation) - 用这个本地URL初始化AVPlayerItem
示例代码(Swift):
guard let mainManifestURL = URL(string: "你的主m3u8URL"), let manifestString = try? String(contentsOf: mainManifestURL) else { return } // 替换第一个AAC 96的配置 let modifiedManifest = manifestString.replacingOccurrences(of: "NAME=\"AAC 96\",DEFAULT=NO,AUTOSELECT=NO", with: "NAME=\"AAC 96\",DEFAULT=YES,AUTOSELECT=YES") let modifiedData = modifiedManifest.data(using: .utf8) let tempURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("temp.m3u8") try? modifiedData?.write(to: tempURL) let playerItem = AVPlayerItem(url: tempURL) let player = AVPlayer(playerItem: playerItem)
5. 增强错误日志排查
为了更精准定位问题,你可以给AVPlayerItem添加状态监听,获取更详细的错误信息:
playerItem.addObserver(self, forKeyPath: "status", options: [.new, .old], context: nil) // 监听回调 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "status", let item = object as? AVPlayerItem { switch item.status { case .failed: if let error = item.error { print("加载失败详细错误:\(error)") print("错误详情:\(error.userInfo)") } default: break } } }
这些日志能帮你确认是Manifest解析失败,还是子轨道加载失败。
内容的提问来源于stack exchange,提问作者Rexam
相关产品推荐
相关产品推荐

