Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVPlayer seek right after it starts to play

I need to jump to particular time in audiofile right after it starts to play. So I use seekToTime method with completion handler

avPlayer.play()

...

avPlayer?.seekToTime(jumpTime, completionHandler: { isComplete in
    if isComplete {
        MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo![MPNowPlayingInfoPropertyElapsedPlaybackTime] = CMTimeGetSeconds((self.avPlayer!.currentItem?.currentTime())!)
    }
})

The problem is that it needs time to start playing file from the internet. And for some reason the version of seekToTime with completion handler crashes the app, because it's invoked before avPlayer started to play. The version without completion handler works fine.

Error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'AVPlayerItem cannot service a seek request with a completion handler until its status is AVPlayerItemStatusReadyToPlay.'

Is there a way to use a callback with avPlayer.play()?

like image 562
ttrs Avatar asked Jul 12 '16 14:07

ttrs


2 Answers

Yes, there is a way.

You need to observe changes in player:

avPlayer?.addPeriodicTimeObserverForInterval(CMTime(value: 1, timescale: 3), queue: dispatch_get_main_queue()) { [weak self] time in
    self?.handleCallback(time)
}

And then you handle the state in the callback:

func handleCallback(time: CMTime) {    
   if avPlayer?.status == .ReadyToPlay {
       // place your seek logic here
   }
}
like image 167
fiks Avatar answered Sep 28 '22 11:09

fiks


Please look at the Apple's answer for it: https://developer.apple.com/library/content/qa/qa1820/_index.html

    class MyClass {

    var isSeekInProgress = false
    let player = <#A valid player object #>
    var chaseTime = kCMTimeZero
    // your player.currentItem.status
    var playerCurrentItemStatus:AVPlayerItemStatus = .Unknown

    ...

    func stopPlayingAndSeekSmoothlyToTime(newChaseTime:CMTime)
    {
        player.pause()

        if CMTimeCompare(newChaseTime, chaseTime) != 0
        {
            chaseTime = newChaseTime;

            if !isSeekInProgress
            {
                trySeekToChaseTime()
            }
        }
    }

    func trySeekToChaseTime()
    {
        if playerCurrentItemStatus == .Unknown
        {
            // wait until item becomes ready (KVO player.currentItem.status)
        }
        else if playerCurrentItemStatus == .ReadyToPlay
        {
            actuallySeekToTime()
        }
    }

    func actuallySeekToTime()
    {
        isSeekInProgress = true
        let seekTimeInProgress = chaseTime
        player.seekToTime(seekTimeInProgress, toleranceBefore: kCMTimeZero,
                toleranceAfter: kCMTimeZero, completionHandler:
        { (isFinished:Bool) -> Void in

            if CMTimeCompare(seekTimeInProgress, chaseTime) == 0
            {
                isSeekInProgress = false
            }
            else
            {
                trySeekToChaseTime()
            }
        })
    }

}
like image 36
Maxim Firsoff Avatar answered Sep 28 '22 10:09

Maxim Firsoff