AVPlayer拖动定位后延迟播放,如何判断缓冲状态并显示加载指示器?
我之前在做AVPlayer音频播放项目时,也碰到过一模一样的问题——拖动进度条后音频迟迟不播放,想加加载指示器却用KVO没搞对。后来摸索出几个靠谱的判断缓冲状态的方法,分享给你:
方法1:监听AVPlayer的两个核心缓冲属性
AVPlayer自带的playbackBufferEmpty和playbackLikelyToKeepUp属性是专门用来判断缓冲状态的,尤其适配拖动场景:
- 当你拖动进度条完成后,AVPlayer开始缓冲目标位置的音频,如果
playbackBufferEmpty变为true,说明当前没有足够的缓冲数据支撑播放,这时候就可以显示加载指示器了。 - 当
playbackLikelyToKeepUp变为true时,意味着缓冲的数据已经足够保证后续播放不会中断,这时候就可以隐藏加载指示器,让音频正常播放。
正确的KVO实现代码如下(以Swift为例):
// 在初始化或player创建完成后添加观察者 override func viewDidLoad() { super.viewDidLoad() // 假设player是你的AVPlayer实例 player.addObserver(self, forKeyPath: "playbackBufferEmpty", options: [.new], context: nil) player.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: [.new], context: nil) } // 实现KVO回调方法 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard let player = object as? AVPlayer else { return } if keyPath == "playbackBufferEmpty" { DispatchQueue.main.async { if player.playbackBufferEmpty { // 显示加载指示器 self.showLoadingIndicator() } } } else if keyPath == "playbackLikelyToKeepUp" { DispatchQueue.main.async { if player.playbackLikelyToKeepUp { // 隐藏加载指示器并恢复播放 self.hideLoadingIndicator() player.play() } } } } // 记得在页面销毁时移除观察者,避免内存泄漏 deinit { player.removeObserver(self, forKeyPath: "playbackBufferEmpty") player.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp") }
⚠️ 注意:拖动操作时,建议先暂停播放器,拖动结束后调用seek(to: targetTime),再尝试播放。这样AVPlayer的缓冲状态会更清晰,KVO也能准确触发。
方法2:监听AVPlayerItem的已缓冲时间范围
如果需要更精准的判断(比如想知道拖动的目标位置是否已经被缓冲),可以监听player.currentItem的loadedTimeRanges属性,它会返回当前已缓冲的所有时间区间。
代码示例:
// 添加观察者到player的currentItem override func viewDidLoad() { super.viewDidLoad() player.currentItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.new], context: nil) } // 处理KVO回调 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard let item = object as? AVPlayerItem, keyPath == "loadedTimeRanges" else { return } // 获取已缓冲的时间范围 let loadedRanges = item.loadedTimeRanges guard let firstRange = loadedRanges.first?.timeRangeValue else { return } let bufferedEndTime = CMTimeGetSeconds(firstRange.start) + CMTimeGetSeconds(firstRange.duration) let currentPlaybackTime = CMTimeGetSeconds(player.currentTime()) // 这里可以结合你拖动的目标时间来判断,比如拖动结束时记录targetTime DispatchQueue.main.async { if currentPlaybackTime > bufferedEndTime { // 当前播放位置超过已缓冲的终点,需要显示加载指示器 self.showLoadingIndicator() } else { // 已缓冲足够,隐藏加载指示器 self.hideLoadingIndicator() } } } // 移除观察者 deinit { player.currentItem?.removeObserver(self, forKeyPath: "loadedTimeRanges") }
这个方法的优势是能直观看到缓冲的进度,适合需要自定义加载逻辑的场景。
内容的提问来源于stack exchange,提问作者Frankenxtein
相关产品推荐
相关产品推荐

