AVPlayer回到前台后缓冲区清空问题技术求助
AVPlayer后台返回后缓冲清空重新加载的问题
我基于AVPlayer开发了一款带有进度条缓冲检测功能的自定义视频播放器,遇到如下问题:暂停视频后将应用退至后台约5秒(主屏幕左右滑动切换),重新打开应用继续播放时,缓冲区会被清空并重新开始缓冲,同时isPlaybackBufferEmpty观察者会被触发。
以下是我的播放器代码:
@IBOutlet weak var progressBar: UIProgressView! @IBOutlet var containerPlayer: PlayerView! @objc var player = AVPlayer() let toPass = "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8" private var bufferObserver: NSKeyValueObservation? private var playerItemBufferEmptyObserver: NSKeyValueObservation? private var playerItemBufferKeepUpObserver: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() containerPlayer.player = self.player containerPlayer.playerLayer.player = self.player startPlayer() } func startPlayer() { if let videoURL = URL(string: self.toPass) { let asset = AVURLAsset(url: videoURL) let item = AVPlayerItem(asset: asset) self.player.replaceCurrentItem(with: item) self.player.currentItem?.preferredPeakBitRate = 0 self.player.currentItem?.preferredForwardBufferDuration = 10 self.player.play() playerItemBufferEmptyObserver = item.observe(\.isPlaybackBufferEmpty, options: [.old, .new]) { [weak self] (playerItem, _) in if playerItem.isPlaybackBufferEmpty { print("playerItem.isPlaybackBufferEmpty") } } playerItemBufferKeepUpObserver = item.observe(\.isPlaybackLikelyToKeepUp, options: [.new]) { [weak self] (playerItem, _) in if playerItem.isPlaybackLikelyToKeepUp { print("playerItem.isPlaybackLikelyToKeepUp") } } bufferObserver = item.observe(\.loadedTimeRanges, options: [.new], changeHandler: { (playerItem, change) in if let timeRange = playerItem.loadedTimeRanges.first?.timeRangeValue { let startSeconds = timeRange.start.seconds let durationSeconds = timeRange.duration.seconds let bufferEnd = startSeconds + durationSeconds let positionTimeElapsed: Float64 = Float64(bufferEnd) / playerItem.asset.duration.seconds self.progressBar.progress = Float(positionTimeElapsed) } }) } } }
问题原因
iOS系统为了优化内存占用,当应用进入后台后会主动回收AVPlayerItem的缓冲资源。当应用回到前台继续播放时,之前的缓存已被系统清理,因此会触发isPlaybackBufferEmpty并重新开始缓冲。
解决方案
1. 监听前后台状态,管理播放器生命周期
注册前后台通知,在后台时暂停播放器并记录播放位置;回到前台时恢复播放并跳转至之前的位置,触发缓冲恢复:
private var currentPlaybackTime: Double = 0 override func viewDidLoad() { super.viewDidLoad() // 原有初始化代码 registerBackgroundNotifications() } private func registerBackgroundNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil) } @objc private func appDidEnterBackground() { player.pause() currentPlaybackTime = player.currentItem?.currentTime().seconds ?? 0 } @objc private func appWillEnterForeground() { guard let item = player.currentItem else { return } player.seek(to: CMTimeMakeWithSeconds(currentPlaybackTime, preferredTimescale: 1)) { [weak self] _ in self?.player.play() } }
2. 复用AVPlayerItem,避免重复创建
将AVPlayerItem设为类属性,仅在首次启动或切换视频时初始化,避免频繁创建导致缓冲丢失:
private var playerItem: AVPlayerItem? func startPlayer() { guard playerItem == nil else { player.replaceCurrentItem(with: playerItem) return } if let videoURL = URL(string: self.toPass) { let asset = AVURLAsset(url: videoURL) playerItem = AVPlayerItem(asset: asset) player.replaceCurrentItem(with: playerItem) // 原有缓冲设置、观察者注册代码 } }
3. 调整缓冲策略(可选)
适当延长preferredForwardBufferDuration,同时可尝试关闭自动 stall 等待(需注意内存占用):
playerItem?.preferredForwardBufferDuration = 30 playerItem?.automaticallyWaitsToMinimizeStalling = false
4. 清理观察者,避免内存泄漏
在控制器销毁时移除所有KVO观察者和通知监听:
deinit { bufferObserver?.invalidate() playerItemBufferEmptyObserver?.invalidate() playerItemBufferKeepUpObserver?.invalidate() NotificationCenter.default.removeObserver(self) }
内容的提问来源于stack exchange,提问作者Ruslan Sabirov
相关产品推荐
相关产品推荐

