Too Many AVPlayers?

Wow, I can’t believe it’s nearly September! For me that means I’m 1) popping allergy pills like a maniac because UGH RAGWEED, 2) getting really excited for the September Apple event, and 3) scrambling to finish up a random side-project app before iOS 11 hits the mainstream.

A couple nights ago I ran into a strange bug with my app, which uses AVFoundation to merge videos. Sometimes I would be able to export the final video with AVAssetExportSession and save it to my photo library, and sometimes it would randomly fail with the following error:

AVFoundationErrorDomain Code=-11839 "Cannot Decode" and NSLocalizedFailureReason=The decoder required for this media is busy., NSLocalizedRecoverySuggestion=Stop any other actions that decode media and try again., NSLocalizedDescription=Cannot Decode

In my app, every time a new video clip is added to the list of videos to be merged, I re-generate a preview of the final merged video. After merging the video, I set up an AVPlayer with the AVComposition like so:

func setupPlayerView(playerItem: AVPlayerItem?) {
        guard self.player == nil && playerItem != nil else { return }
        
        // Create an AVPlayer and AVPlayerLayer with the AVPlayerItem.
        self.player = AVPlayer(playerItem: playerItem)
        let playerLayer = AVPlayerLayer(player: player)
        
        // Configure the AVPlayerLayer and add it to the view.
        playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
        playerLayer.frame = (self.videoPreviewView?.bounds)!
        self.videoPreviewView?.layer.addSublayer(playerLayer)
        
        ...
}

I always made sure to set the AVPlayer to nil before re-generating the preview, so I couldn’t figure out why there would be any other “actions that decode media.” A trip to Stack Overflow revealed a possible platform limitation on the number of video “render pipelines” shared between apps on the device. It turns out that setting the AVPlayer to nil does not free up a playback pipeline and that it is actually the association of a playerItem with a player that creates the pipeline in the first place. Since developers don’t seem to have any control over when these resources are released, I knew I’d have to figure out another solution.

In the end, I decided to initialize the view controller’s AVPlayer right off the bat with its playerItem set to nil. Then I changed my setup function like so:

        // Create an AVPlayer and AVPlayerLayer with the AVPlayerItem.
        self.player?.replaceCurrentItem(with: playerItem)
        let playerLayer = AVPlayerLayer(player: player)

Replacing the player’s playerItem instead of initializing it with a new playerItem each time (even though the player was previously set to nil), seems to prevent that weird “cannot decode” error (so far, at least). I’d like to know more about this error and why exactly it occurs, just out of nerdy curiosity.

Anyway, I hope this helps somebody out!

One thought on “Too Many AVPlayers?

Comments are closed.