Article

Observing the Playback Time

Observe the playback time for an asset in order to update the player's state.

Overview

You’ll commonly want to observe an asset's playback time as it progresses so you can update the playback position or otherwise synchronize the state of your user interface. Although key-value observing (KVO) works well for general state observations, it's not the right choice for observing player timing because it’s not well suited for observing continuous state changes. Instead, AVPlayer provides two different ways for you to observe player time changes: periodic observations and boundary observations.

Observe Periodic Timing

You can observe time ticking by at some regular, periodic interval. If you’re building a custom player, the most common use case for periodic observation is to update the time display in your user interface.

To observe periodic timing, use the player’s addPeriodicTimeObserverForInterval:queue:usingBlock: method. This method takes a CMTime value representing the time interval, a serial dispatch queue, and a callback block to be invoked at the specified time interval. The following example shows how to set up a block to be called every half-second during normal playback:

var player: AVPlayer!
var playerItem: AVPlayerItem!
var timeObserverToken: Any?

func addPeriodicTimeObserver() {
    // Notify every half second
    let timeScale = CMTimeScale(NSEC_PER_SEC)
    let time = CMTime(seconds: 0.5, preferredTimescale: timeScale)

    timeObserverToken = player.addPeriodicTimeObserver(forInterval: time,
                                                      queue: .main) {
        [weak self] time in
        // update player transport UI
    }
}

func removePeriodicTimeObserver() {
    if let timeObserverToken = timeObserverToken {
        player.removeTimeObserver(timeObserverToken)
        self.timeObserverToken = nil
    }
}

Observe Boundary Timing

The other way you can observe time is by boundary. You define various points of interest within the media’s timeline, and the framework calls you back as those times are traversed during normal playback. Boundary observations are used less frequently than periodic observations, but can still prove useful in certain situations. For instance, you might use boundary observations if you're presenting a video with no playback controls and want to synchronize elements of the display or present supplemental content as those times are traversed.

To observe boundary times, use the player’s addBoundaryTimeObserverForTimes:queue:usingBlock: method. This method takes an array of NSValue objects wrapping the CMTime values that define your boundary times, a serial dispatch queue, and a callback block. The following example shows how to define boundary times for each quarter of playback:

var asset: AVAsset!
var player: AVPlayer!
var playerItem: AVPlayerItem!
var timeObserverToken: Any?

func addBoundaryTimeObserver() {

    // Divide the asset's duration into quarters.
    let interval = CMTimeMultiplyByFloat64(asset.duration, 0.25)
    var currentTime = kCMTimeZero
    var times = [NSValue]()

    // Calculate boundary times
    while currentTime < asset.duration {
        currentTime = currentTime + interval
        times.append(NSValue(time:currentTime))
    }

    timeObserverToken = player.addBoundaryTimeObserver(forTimes: times,
                                                       queue: .main) {
        // Update UI
    }
}

func removeBoundaryTimeObserver() {
    if let timeObserverToken = timeObserverToken {
        player.removeTimeObserver(timeObserverToken)
        self.timeObserverToken = nil
    }
}

See Also

Media Playback

Responding to Playback State Changes

Respond to changes in a player's playback state.

Seeking Through Media

Seek or scrub across a media item to quickly access a specific time point.

Asset Playback

Play audio or video assets modeled by a player item.

QuickTime and ISO-Related Media

Create, play, and modify QuickTime and ISO-related media assets.