无法播放Documents目录离线音频,AVPlayerItem状态为unknown求助
离线音频播放AVPlayerItem status=unknown问题排查
核心问题点
- 资源未主动触发加载:创建
AVAsset和AVPlayerItem后,未触发资源加载流程,AVPlayerItem默认不会自动加载本地资源的可播放状态,导致status一直停留在unknown。 - 观察者绑定时机错误:在初始化AVPlayer之后才尝试给currentItem加观察者,若currentItem未完成初始化,观察者无法绑定成功,自然接收不到状态变化。
- 可选值处理不严谨:
currentItem as! Book强制解包存在崩溃风险;audioPlayer?.currentItem?.addObserver的可选链如果currentItem为nil,观察者根本不会被添加。
修正后的代码实现
优化音频路径获取函数
func getAudioUrl(book: Book) -> URL? { guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { print("无法访问Documents目录") return nil } // 用格式化字符串避免拼接错误,更易维护 let fileName = String(format: "test-audio-%ld.mp3", book.id) let fileUrl = documentsDirectory.appendingPathComponent(fileName) guard FileManager.default.fileExists(atPath: fileUrl.path) else { print("目标文件不存在:\(fileUrl.path)") return nil } return fileUrl }
修正播放初始化流程
// 安全解包,避免强制类型转换崩溃 guard let currentBook = currentItem as? Book, let fileURL = OfflineHandler.sharedInstance.getAudioUrl(book: currentBook) else { print("无法获取有效音频文件路径") return } let asset = AVAsset(url: fileURL) // 异步加载资源的可播放状态,确保资源就绪后再初始化Player asset.loadValuesAsynchronously(forKeys: ["playable", "duration"]) { [weak self] in guard let self = self else { return } var loadError: NSError? let playableStatus = asset.statusOfValue(forKey: "playable", error: &loadError) switch playableStatus { case .loaded: // 资源加载完成,先给PlayerItem加观察者,再关联到AVPlayer let avPlayerItem = AVPlayerItem(asset: asset) avPlayerItem.addObserver(self, forKeyPath: "status", options: [.new, .initial], context: nil) // 绑定播放完成通知 NotificationCenter.default.addObserver(self, selector: #selector(self.itemDidFinishPlaying(_:)), name: .AVPlayerItemDidPlayToEndTime, object: avPlayerItem) self.audioPlayer = AVPlayer(playerItem: avPlayerItem) self.audioPlayer?.volume = 1.0 self.audioPlayer?.automaticallyWaitsToMinimizeStalling = false self.updateCC() self.isReadyToPlay = true // 资源就绪后触发播放 self.audioPlayer?.play() case .failed: print("资源加载失败:\(loadError?.localizedDescription ?? "未知错误")") case .cancelled: print("资源加载被取消") default: print("资源加载状态异常:\(playableStatus.rawValue)") } }
完善观察者与内存管理
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { // 仅处理AVPlayerItem的status变化,避免干扰其他观察者 guard let playerItem = object as? AVPlayerItem, keyPath == "status" else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } switch playerItem.status { case .readyToPlay: print("音频已准备就绪") case .failed: print("音频播放失败:\(playerItem.error?.localizedDescription ?? "未知错误")") case .unknown: print("音频状态未知,可能加载未完成") @unknown default: print("未处理的状态:\(playerItem.status.rawValue)") } } // 页面销毁时移除观察者和通知,避免内存泄漏 deinit { audioPlayer?.currentItem?.removeObserver(self, forKeyPath: "status") NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: nil) }
内容的提问来源于stack exchange,提问作者Chris Hansen
相关产品推荐
相关产品推荐

