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

AVPlayer addPeriodicTimeObserverForInterval方法持续调用不停止问题排查

分析AVPlayer时间观察者持续调用导致播放时长异常增长的原因

结合你提供的代码和问题描述,这种playingSeconds远超歌曲实际时长的情况,主要是因为AVPlayer的周期性时间观察者没有按预期停止,或者播放状态的判断逻辑存在漏洞。下面是几个最可能的原因及排查方向:


1. 时间观察者未被正确移除

AVPlayer的addPeriodicTimeObserverForInterval:方法返回的观察者对象,必须在不需要时主动调用removeTimeObserver:销毁。如果你的播放器在以下场景中没有移除观察者:

  • 歌曲自然播放完毕后
  • 手动暂停/停止播放时
  • 切换到下一首歌曲时
  • 播放器实例被释放前

旧的观察者会一直监听时间变化,哪怕歌曲已经结束,甚至播放器被复用。极端情况下如果多次添加观察者而不移除,还会出现多个回调同时执行,导致playingSeconds被多次累加。

排查建议:
在playerItem的AVPlayerItemDidPlayToEndTimeNotification回调、暂停/停止方法中,添加移除观察者的代码:

if (self.playbackTimeObserver) {
    [self.player removeTimeObserver:self.playbackTimeObserver];
    self.playbackTimeObserver = nil;
}

2. 播放状态self.state未正确更新

你的代码中只有当self.state == BPAudioPlayerStatePlaying && !self.isBuffering时,才会累加playingSeconds。如果歌曲播放完毕后,self.state没有被正确切换为非播放状态(比如BPAudioPlayerStateStopped或BPAudioPlayerStatePaused),这个判断条件会一直为真,导致回调持续执行、playingSeconds疯涨。

常见的异常场景:

  • 单曲循环逻辑中,歌曲结束后没有先更新状态再重新播放
  • 播放器遇到解码错误或资源异常,但未触发状态变更
  • 后台播放恢复时,状态同步失败

排查建议:
监听AVPlayerItemDidPlayToEndTimeNotification,在回调中强制更新播放状态,并根据循环逻辑决定是否重新播放:

- (void)playerItemDidFinishPlaying:(NSNotification *)notification {
    self.state = BPAudioPlayerStateStopped;
    // 处理单曲循环逻辑
    if (self.isLoop) {
        [self.player seekToTime:kCMTimeZero];
        self.state = BPAudioPlayerStatePlaying;
        [self.player play];
    }
    // 移除旧观察者,循环播放时重新添加(如果需要)
    [self removeTimeObserver];
    if (self.isLoop) {
        [self addTimeObserver];
    }
}

3. playerItem的时间参数异常

代码中计算totalDuration和currentTime时,没有判断CMTime的有效性:

  • 如果playerItem.duration是kCMTimeInvalid(比如资源加载失败、网络错误),self.totalDuration会被错误设置为0,导致ct <= self.totalDuration的判断失效
  • 如果currentTime出现异常值(比如无限循环的时间戳),会绕过时长判断,持续触发累加逻辑

排查建议:
在计算时长前先判断CMTime的有效性:

if (self.totalDuration == 0 && CMTIME_IS_VALID(self.playerItem.duration)) {
    self.totalDuration = self.playerItem.duration.value / self.playerItem.duration.timescale;
    [self updateNowPlayingInfo:YES];
}

同时给playingSeconds的累加加一个兜底判断:

if (self.state == BPAudioPlayerStatePlaying && !self.isBuffering) {
    if (ct <= 0) {
        self.playingSeconds = 0;
    } else if (self.playingSeconds < self.totalDuration) { // 兜底:不超过歌曲总时长
        self.playingSeconds += 1;
    }
}

4. 后台播放或系统中断后的状态异常

如果你的APP支持后台播放,以下场景可能导致观察者持续运行:

  • 后台播放时,音频会话被系统临时占用(比如来电、 Siri),恢复后播放器状态未同步
  • APP进入后台后,AVPlayer的时间观察者没有被正确暂停,仍在后台执行回调

排查建议:
监听音频会话的状态变化(AVAudioSessionInterruptionNotification),在中断开始时移除观察者,中断结束后根据状态重新添加:

- (void)handleAudioSessionInterruption:(NSNotification *)notification {
    NSDictionary *info = notification.userInfo;
    AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (type == AVAudioSessionInterruptionTypeBegan) {
        [self removeTimeObserver];
    } else if (type == AVAudioSessionInterruptionTypeEnded) {
        if (self.state == BPAudioPlayerStatePlaying) {
            [self addTimeObserver];
            [self.player play];
        }
    }
}

内容的提问来源于stack exchange,提问作者Carry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:33:28