AVPlayerItem replaceCurrentItemWithPlayerItem Blocking

Aatish Molasi picture Aatish Molasi · Jan 29, 2013 · Viewed 7.6k times · Source

First a little context about the application...
- There's a lot of heavy UI operation's involving video players (mostly scrolling)
- The videos are dynamic and change based on our current page .
- so the video's have to be dynamic and keep changing and also the UI needs to be responsive

I was initially using a MPMoviePlayerController but then due to certain requirements i had to fall back on the AVPlayer
I made my own wrapper for the AVPlayer .
To change the content in the videoPlayer this is what the method looks like in the AVPlayer-wrapper Class

/**We need to change the whole playerItem each time we wish to change a video url */
-(void)initializePlayerWithUrl:(NSURL *)url
{
    AVPlayerItem *tempItem = [AVPlayerItem playerItemWithURL:url];

    [tempItem addObserver:self forKeyPath:@"status"
                  options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                  context:nil];
    [tempItem addObserver:self forKeyPath:@"playbackBufferEmpty"
                  options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                  context:nil];

    //Not sure if this should be stopped or paused under the ideal circumstances
    //These will be changed to custom enums later
    [self setPlaybackState:MPMoviePlaybackStateStopped];
    [self setLoadState:MPMovieLoadStateUnknown];
    [self.videoPlayer replaceCurrentItemWithPlayerItem:tempItem];

    //This is required only if we wish to pause the video immediately as we change the url
    //[self.videoPlayer pause];
}

Now ofcourse everything was working fine ......except ..

[self.videoPlayer replaceCurrentItemWithPlayerItem:tempItem];

Seems to be blocking the UI for a fraction of a second and during scrolling these is making the UI really unresponsive and ugly also this operation cannot be performed in the background

Is there any fix or workaround for this .. ?

Answer

Glenn Schmidt picture Glenn Schmidt · Sep 23, 2015

The solution I found was to ensure that the underlying AVAsset is ready to return basic info, such as its duration, before feeding it to the AVPlayer. AVAsset has a method loadValuesAsynchronouslyForKeys: which is handy for this:

AVAsset *asset = [AVAsset assetWithURL:self.mediaURL];
[asset loadValuesAsynchronouslyForKeys:@[@"duration"] completionHandler:^{
    AVPlayerItem *newItem = [[AVPlayerItem alloc] initWithAsset:asset];
    [self.avPlayer replaceCurrentItemWithPlayerItem:newItem];
}];

In my case the URL is a network resource, and replaceCurrentItemWithPlayerItem: will actually block for several seconds waiting for this information to download otherwise.