AVPlayerItem fails with AVStatusFailed and error code "Cannot Decode"

I'm running into a strange issue, I hope someone can help.

In my iOS app I create a video with a custom soundtrack using MutableComposition by combining a video from the user's photo library and an audio file from the app bundle. I then use an AVPlayer and AVPlayerItem to play the video back to the user using a custom video player I made.

Each time a new composition is created, the assets, the player and the composition are cleared, released and it basically starts from a clean, init state.

All works fine, until after exactly 4 successful videos created this way every other attempt to create the player fails with error Cannot Decode. It does not matter if its the same video I'm recreating, has no relation to the size/length of the video or the audio file it simply always fails exactly on the fifth attempt, like clockwork. Once it fails, it will then always fail!

This is weird, because it just decoded the same video four times with no problem, so all of a sudden it fails? So, if anyone has a clue, please let me know.


Ok everyone, I have the answer to this straight from Apple. I used one of my developer TSI lifelines to ask the question, and I'll summarize the response.

There is a limit on the number of concurrent video players that AVFoundation will allow. It is due to the limitations of iOS hardware. The limit for current devices is 4 players. If you create a 5th player, you will get the "cannot decode" error. It is not a limit on the number of instances of AVPlayer, or AVPlayerItem. Rather,it is the association of AVPlayerItem with an AVPlayer which creates a "render pipeline", and you are limited to 4 of these. For example, this causes a new render pipeline:

AVPlayer *player = [AVPlayer playerWithPlayerItem:somePlayerItem];  
// assuming the AVPlayerItem is ready to go with an AVAsset that has been loaded

I was also warned that you cannot assume that you will have 4 pipelines available to you. Another App may be using one or more. Indeed, I have seen this happen on an iPad, but it was not clear which app was using a pipeline.

So, there you go, it was totally undocumented, but that is the story.


I ran into the same error message after creating 4 AVPlayer instances, the fix in my case wasn't exactly the same though. Perhaps this will help anyone else who comes across this problem.

What I eventually found is that the AVPlayers were not being released when I had thought they were. In my case I was pushing my AVPlayer View Controller onto a Navigation Controller. Even though I was only creating one AVPlayer instance at a time, when the View Controllers are popped off a nav controller they were not being released immediately. It was then very easy for me to reach 4 AVPlayer instances before the old View Controllers were cleaned up.

It wasn't until I made sure that the previous players were released that this problem went away. To be complete I released the AVPlayerItem, AVPlayer and set the player on the AVPlayerLayer to nil before releasing.

I have to wonder if there is some limit on AVPlayer instances, unintentional or not. A related bit of info from the docs: https://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/02_Playback.html

"Multiple player layers: You can create arbitrarily many AVPlayerLayer objects from a single AVPlayer instance, but only the most-recently-created such layer will display any video content on-screen."


This one was absolutely killing me until I figured it out, picking up clues from this thread and a few others. The biggest single problem in my code was that I was instantiating my video player controller every time I wanted to play a video. Now, it gets instantiated once in the primary controller (in this case, my DetailViewContoller):

@interface DetailViewController () {
    VideoPlayerViewController *videoPlayerViewController;
}

- (void) viewDidLoad
{
    [super viewDidLoad];

    videoPlayerViewController = [[VideoPlayerViewController alloc] initWithNibName: nil bundle: nil];
}

When I want to show a video, I call my DetailViewController's startVideoPlayback method:

- (void) startVideoPlayback: (NSString *)videoUID
{
    videoPlayerViewController.videoUID = videoUID;
    [self presentModalViewController: videoPlayerViewController animated: YES];
}

(NOTE: I'm passing it 'videoUID' -- a unique identified that was used to create the video in another part of the app.)

In the VideoPlayerViewController (which is largely cribbed from Apple's AVPlayerDemo sample), the one-time screen setup (initializing the AVPlayer, setting up the toolbar, etc.) is done in viewDidLoad -- which now only get's called once, and all per-video setup gets done within viewWillAppear, which then calls prepareToPlay:

- (void) prepareToPlay
{
    [self initScrubberTimer];   
    [self syncPlayPauseButtons];
    [self syncScrubber];

    NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    //*** Retrieve and play video at associated with this videoUID
    NSString *destinationPath = [documentsDirectory stringByAppendingFormat: @"/%@.mov", videoUID];
    if ([self fileExists: destinationPath]) {

        //*** Show the activity indicator spinny thing
        [pleaseWait startAnimating];
        [self setURL: [NSURL fileURLWithPath: destinationPath]];
        //*** Get things going with the first video in this session
        if (isFirst) {
            isFirst = NO;
        //*** Subseqeunt videos replace the first one
        } else {
            [self.mPlayer replaceCurrentItemWithPlayerItem: [AVPlayerItem playerItemWithURL: [NSURL fileURLWithPath: destinationPath]]];
        }
    }
}