Zhiqiang Li
Zhiqiang Li

Reputation: 411

how to improve the performance of seek when using avplayer

I'm using AVPlayer to create a video player, but the seekToTime method is pretty slow. I'm impressed by the seeking performance of Apple's app "Photos". Does anyone have any idea how Apple managed to do such a quick seeking?

Does it has anything to do with threads? I tried to put the seekToTime call in a dispatch queue, it does't help either.

Upvotes: 5

Views: 2741

Answers (3)

defagos
defagos

Reputation: 561

Achieving smooth seeking like what Apple is able to do in its standard iOS user interface requires a combination of several tricks:

  1. As said above you need to chain seeks without cancelling pending ones. This is essential as otherwise the player won't be able to move forward if seek requests keep getting cancelled.
  2. The player must be paused during the seek operation. You can restore the intended playback rate when the last seek request ends.
  3. Depending on AVPlayerItem declared capabilities you should adjust seek tolerances as follows:
  • If the item returns true from canStepForward then seek with .zero for both tolerances. This provides for a step-by-step seek user experience for such content.
  • If the item returns true from canPlayFastForward then seek with .zero as toleranceBefore and .positiveInfinity as toleranceAfter. This ensures that the player can use available I-frame playlists for a snappier seek user experience (trick mode).
  • Otherwise just seek with .positiveInfinity for both tolerances, as this ensures the player can reach the desired location as fast as possible (but without additional benefits).

Note that the above is not officially documented by Apple AFAIK. I simply had a look at AVPlayerViewController behavior on iOS 16+, observing how the attached AVPlayer is used when seeking within different kinds of streams.

You can check our implementation on GitHub for implementation details.

Upvotes: 2

Petro Novosad
Petro Novosad

Reputation: 129

This code is taken from: https://developer.apple.com/library/archive/qa/qa1820/_index.html

It helps a little and seek forward looks smooth. But seek backward still tooks too many time (here SeekToTime working smoothly just for forwards, freezy on backwards is explanation why).

import AVFoundation
 
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()
            }
        })
    }
 
}

Upvotes: 0

Zhiqiang Li
Zhiqiang Li

Reputation: 411

I have found the solution.

If I use seekToTime to do the scrubbing, it's pretty slow. What I should use is a method called stepByCount from AVPlayerItem.

Upvotes: 2

Related Questions