22

This works: I have an AVPlayer which plays a video right after it was loaded. And this works fine.

This doesn't:

Instead of starting the video at the beginning, I want to play it at a given position. So I wait until the asset is ready for Play using KVO:

BOOL isReadyToSeek = (self.playerItem.status == AVPlayerStatusReadyToPlay)

And then seek to the given time

[playerItem seekToTime:timeInTheMiddleOfTheVideo completionHandler:myHandler];

But the player will always start at the beginning of the video on the initial seek.

Update I tried a dispatch_after with a few seconds but that does not work either. It works after playback but never initially.

When I observe self.player.currentItem.duration I keep getting 0 as the value and timescale.

Besi
  • 22,579
  • 24
  • 131
  • 223
  • Are you saying it works if you delay it? – Carl Veazey Aug 26 '12 at 21:58
  • Are you sure you're passing a valid time argument? What's the value of timeInTheMiddleOfTheVideo? – Carl Veazey Aug 26 '12 at 22:07
  • The video is approx 30 min. I also tried CMTimakewotgseconds(15,1) which is definetely inside the bounds – Besi Aug 26 '12 at 22:08
  • You say it works after playback. Maybe a stupid question on my end but what happens if you play and then immediately seek? – Carl Veazey Aug 26 '12 at 22:13
  • Not a stupid question at all :-) The problem is because setting a slider will trigger the initial playback, so I don't want to start from the beginning unless the slider is set to the start of the track... – Besi Aug 26 '12 at 22:35
  • have you been able to solve this issue ? please update the question with your findings. many thanks. – rajneesh Feb 10 '13 at 13:40

1 Answers1

51

Couple of things which may help you:

  1. When checking if the player is ready to play, I check both the player itself and also the player's current item.

    if(self.player.status == AVPlayerStatusReadyToPlay &&
       self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
        [self.player play];
    }
    
  2. It's possible that if you seek to e.g. 15 seconds, it may start from the beginning of the video instead if that is the location of the nearest keyframe. To force the player to seek to the exact location (note: this will take several seconds to do), do this:

    Float64 seconds = 500.0f;
    CMTime targetTime = CMTimeMakeWithSeconds(seconds, NSEC_PER_SEC);
    [self.player seekToTime:targetTime
     toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
    

    Or you can seek to the keyframe explicitly before/after the specified point by specifying one of the arguments as infinity:

    [self.player seekToTime:targetTime
     toleranceBefore:kCMTimePositiveInfinity toleranceAfter:kCMTimeZero];
    
  • Isn't AVPlayerStatusReadyToPlay would be an ultimate way to check? Why do you check for item status? – pronebird Apr 16 '14 at 12:13
  • 1
    I ran into cases where the player would be ready but the item is not ready, or vice versa. The `[AVPlayer status]` documentation says "Indicates that the player is ready to play AVPlayerItem instances." and the `[AVPlayerItem status]` "The item is ready to play.". So there is a difference between being able to play items, and the current item actually being ready. –  Apr 17 '14 at 02:32
  • 1
    I use AVPlayerItemStatusReadyToPlay only – onmyway133 Sep 06 '14 at 14:55
  • Great answer. Especially for shorter videos, the kCMTimeZero tolerance does not have any delay and shows a very smooth transition. – trdavidson Sep 20 '15 at 19:27
  • You are a god. Very good explanation and great example. It works like a charm! – anonymous May 22 '17 at 10:56
  • You can call [AVPlayer play] even when there is no AVPlayerItem in the player yet. Player records intention and when you attach playerItem, you save few milliseconds because player doesn't need to generate static image. – Juraj Antas Apr 25 '18 at 18:01