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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 14:17:54