Swift iOS -CMTimeMakeWithSeconds: warning: error of -0.433 introduced due to very low timescale
Asked Answered
K

1

5

I'm using AVPlayer to play a video. I followed this video tutorial Let's Build That App

I use a UISlider/scrubber to go along with the current frame/time of the video.

I have a video that is 10.43 seconds and I use a fast forward function that takes me to the very end.

@objc fileprivate func fastForwardButtonTapped() {

        guard let playerItem = playerItem else { return }
        guard let player = player else { return }

        let duration: Float64 = CMTimeGetSeconds(playerItem.duration)
        let seekTime: CMTime = CMTimeMakeWithSeconds(duration, 1)
        player.seek(to: seekTime)
}

The video goes to the very end but the problem is the slider only goes to the 10 sec point and I cannot get it to go to the last .43 secs. I get a warning message of:

enter image description here

The slider's value is determined in the player?.addPeriodicTimeObserver() And because of this when I press fast forward instead of the slider going to the very end it stops a couple of points away (notice the white space):

enter image description here

How can I get more accurate values so that my slider can scrub all the way to the end?

playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status),
                                options: [.old, .new],
                                context: &playerItemContext)

let interval = CMTime(value: 1, timescale: 2)

timeObserverToken = player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: {
    [weak self] (progressTime) in

    let seconds = CMTimeGetSeconds(progressTime)

    let secondsString = String(format: "%02d", Int(seconds) % 60)
    let minutesString = String(format: "%02d", Int(seconds) / 60)

    self?.currentTimeLabel.text = "\(minutesString):\(secondsString)"

    if let duration = self?.playerItem!.duration{

        let durationSeconds = CMTimeGetSeconds(duration)
        self?.slider.value = Float(seconds / durationSeconds) // SLIDER IS UPDATED HERE
    }
})
Kynewulf answered 15/3, 2018 at 4:2 Comment(0)
K
11

I did some research and AVPlayer has a seek method on it:

player.seek(to: <CMTime>, toleranceBefore: <CMTime>, toleranceAfter: <CMTime>)

With this method you can set a tolerance on it to compensate for the truncated overflow which in my situation was the additional -0.433 seconds.

In the first argument you put the time your seeking to and in the second and third arguments you put in kCMTimeZero. Like this:

// seekTime is the time I’m seeking to
player.seek(to: seekTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)

I also followed this answer and the trick was when I initialized my seek time for the second argument I had to put 1000 for everything to work. Like this:

let seekTime: CMTime = CMTimeMakeWithSeconds(duration, 1000)

Here's the code for my fast forward button:

@objc fileprivate func fastForwardButtonTapped() {

        guard let playerItem = playerItem else { return }
        guard let player = player else { return }

        let duration: Float64 = CMTimeGetSeconds(playerItem.duration)
        let seekTime: CMTime = CMTimeMakeWithSeconds(duration, 1000)
        player.seek(to: seekTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
}
Kynewulf answered 17/3, 2018 at 7:41 Comment(2)
Or you can use CMTimeScale(NSEC_PER_SEC) instead of 1000.Tomato
What’s the benefit of using that?Kynewulf

© 2022 - 2024 — McMap. All rights reserved.