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

AVPlayer异常咨询:asset时长小于当前时间,seek代码是否为诱因?

关于AVPlayer 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:41:49