AVPlayer Seek not accurate even with toleranceBefore: kCMTimeZero

2.3k Views Asked by At

We have an app which plays a long mp3 file (1 hour long). We want to be able to play from set points within the file. But, when we do it, it is inaccurate by up to 10 seconds.

Here's the code:

  let trackStart = arrTracks![MediaPlayer.shared.currentSongNo].samples

  let frameRate : Int32 = (MediaPlayer.shared.player?.currentItem?.asset.duration.timescale)!

  MediaPlayer.shared.player?.seek(to: CMTimeMakeWithSeconds(Double(trackStart), frameRate), 
    toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
  • We have to use AVPlayer because we need the better quality "spectral:" AVAudioTimePitchAlgorithm.
  • We didn't have the problem with AVAudioPlayer, but (AFAIK) we have to use AVPlayer because we need the better quality "spectral:" AVAudioTimePitchAlgorithm.

  • [Edit:] - The error is consistent - it always plays from the same (wrong) place for a given requested position. This is also true after restarting.

Any help very much appreciated! Thanks

[Edit:]

  • We have already tried preferredTimescale: playerTimescale
  • Also tried kCMTimeIndefinite instead of kCMTimeZero
3

There are 3 best solutions below

0
On BEST ANSWER

This is what AVURLAsset’s AVURLAssetPreferPreciseDurationAndTimingKey is for.

Apple's documentation.

Beware that this should increase the loading time.

1
On

I have done something similar but with a slider to change seconds of playing and worked perfectly.

 @objc func handleSliderChange(sender: UISlider?){
        if let duration = player?.currentItem?.duration{
            let totalSeconds = CMTimeGetSeconds(duration)
            let value = Float64(videoSlider.value) * totalSeconds
            let seekTime = CMTime(value: CMTimeValue(value), timescale: 1)
            player?.seek(to: seekTime , completionHandler: { (completedSeek) in
                //do smthg later
            })
        }
    }

in you case this will be like this:

let trackStart = arrTracks![MediaPlayer.shared.currentSongNo].samples
let value = Float64(trackStart)
let seekTime = CMTime(value: CMTimeValue(value), timescale: 1)
MediaPlayer.shared.player?.seek(to: seekTime , completionHandler: { (completedSeek) in
                //do smthg later
            })
1
On

Try this, It's working perfectly for me

@IBAction func playbackSliderValueChanged(_ playbackSlider: UISlider) {
    let seconds : Int64 = Int64(playbackSlider.value)
    let targetTime: CMTime = CMTimeMake(value: seconds, timescale: 1)
    DispatchQueue.main.async {
        self.player!.seek(to: targetTime)
        if self.player!.rate == 0 { // if the player is not yet started playing
            self.player?.play()
        }
    }
}