AVPlayer异常咨询:asset时长小于当前时间,seek代码是否为诱因?
嘿,这个问题我之前做视频播放器开发时也碰到过类似情况,咱们来一步步捋清楚~
是不是你的seek代码导致的问题?
没错,你当前使用的seek写法很大概率就是引发这个异常的核心原因,主要有两个关键点:
1. 严格到极致的时间容差拖慢了状态同步
你设置了toleranceBefore: kCMTimeZero和toleranceAfter: kCMTimeZero,这相当于强制AVPlayer必须精准定位到指定时间点。但AVPlayer在处理媒体资源(尤其是流媒体、分段加载的视频或者非本地文件)时,这种严格要求会让播放器在seek后难以快速同步到稳定状态——有时候你的periodicTimeObserver已经触发了,播放器却还没来得及更新最新的媒体时长信息,这时候读取到的duration还是旧值,而当前时间已经被seek到了接近甚至超过旧duration的位置,自然就出现了时长小于当前时间的情况。
2. 盲目叠加20秒可能超出有效播放范围
你基于avPlayer.currentTime()直接加20秒作为seek目标,但如果当前时间已经接近媒体的实际尾部,加上20秒后就会超出有效播放时长。虽然AVPlayer理论上会自动把seek位置修正到媒体末尾,但这个修正过程和duration的更新之间存在时序差:periodicTimeObserver可能在duration还没更新到实际末尾值时,就先读取到了已经被修正后的当前时间,这时候就会出现duration < currentTime的异常。
给你几个可行的解决方案
1. 放宽时间容差,优先保证状态稳定
除非你有绝对严格的精准定位需求(比如视频剪辑场景),否则完全没必要用kCMTimeZero作为容差。改用合理的容差范围,既能保证seek精度足够用,又能让播放器更高效地完成定位:
let seekTime = avPlayer.currentTime() + CMTimeMake(value: 20, timescale: 1) // 设置±0.1秒的容差,平衡精度和性能 avPlayer.seek(to: seekTime, toleranceBefore: CMTimeMake(value: 1, timescale: 10), toleranceAfter: CMTimeMake(value: 1, timescale: 10))
2. 用带完成回调的seek方法,等待状态稳定后再处理
改用带completionHandler的seek重载方法,在seek完成后再去处理时长相关的逻辑,避免读取到未更新的旧值:
let seekTime = avPlayer.currentTime() + CMTimeMake(value: 20, timescale: 1) avPlayer.seek(to: seekTime, toleranceBefore: CMTimeMake(value: 1, timescale: 10), toleranceAfter: CMTimeMake(value: 1, timescale: 10)) { [weak self] isFinished in guard let self = self, isFinished else { return } // 在这里处理时长相关逻辑,确保已经拿到最新的duration guard let duration = self.avPlayer.currentItem?.duration, !CMTimeIsInvalid(duration), CMTimeCompare(duration, .zero) != 0 else { return } let currentTime = self.avPlayer.currentTime() // 执行你的业务逻辑,比如更新进度显示等 }
3. 在periodicTimeObserver里增加有效性校验
为了保险起见,在你的时间观察者回调里先做一层校验,避免处理无效的时长数据,甚至可以手动修正异常情况:
// 添加时间观察者时的代码 let timeObserver = avPlayer.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 10), queue: DispatchQueue.main) { [weak self] currentTime in guard let self = self, let duration = self.avPlayer.currentItem?.duration, !CMTimeIsInvalid(duration), CMTimeCompare(duration, .zero) != 0 else { // 时长无效时跳过逻辑,避免异常 return } // 若出现当前时间超过时长的异常,手动修正到媒体末尾 if CMTimeCompare(currentTime, duration) > 0 { self.avPlayer.seek(to: duration) } // 这里再执行你的正常业务逻辑,比如更新进度条 }
另外补充一句:如果是流媒体资源(比如HLS直播、增量加载的视频),时长本身可能会动态变化,这时候更要注意不要缓存duration值,每次使用都要重新获取并校验有效性哦~
内容的提问来源于stack exchange,提问作者Narek Simonyan

