I spent some time debugging a strange issue today where outputMediaDataWillChange was not being consistently called in my AVPlayerItemOutputPullDelegate implementation. Without this delegate function behaving properly it is impossible to suspend and resume the CADisplayLink driving our display updates.

On looking at the initial configuration of the AVPlayerItemOutput, I noticed a race condition:

func configurePlayer(player: AVPlayer) {
    guard let playerItem = player.currentItem else {
        return
    }

    videoOutput.suppressesPlayerRendering = true

    // Please notify me (the delegate) of changes to the media pixel buffer
    videoOutput.requestNotificationOfMediaDataChangeWithAdvanceInterval(advanceInterval)

    // Set the delegate (bad)
    videoOutput.setDelegate(self, queue: dispatch_get_main_queue())

    playerItem.addOutput(videoOutput)
}

With the above code it is completely possible (and happens with some frequency) for the AVPlayer to attempt a call to its video outputs’ outputMediaDataWillChange functions.

func configurePlayer(player: AVPlayer) {
    guard let playerItem = player.currentItem else {
        return
    }

    videoOutput.suppressesPlayerRendering = true

    videoOutput.setDelegate(self, queue: dispatch_get_main_queue())
    videoOutput.requestNotificationOfMediaDataChangeWithAdvanceInterval(advanceInterval)

    playerItem.addOutput(videoOutput)
}

This works but it remains pretty strange as you wouldn’t expect the order of operations to matter before the output is added to an AVPlayerItem. The world is a weird place.