Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVPlayerItem replaceCurrentItemWithPlayerItem Blocking

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 .. ?

like image 370
Aatish Molasi Avatar asked Jan 29 '13 09:01

Aatish Molasi


2 Answers

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.

like image 164
Glenn Schmidt Avatar answered Nov 15 '22 21:11

Glenn Schmidt


We had the same problem when building Ultravisual. I can't exactly remember how we solved it, but IIRC it involved doing as much of the item setup as possible on a background thread, and waiting until the new item reports that it is 'ready to play' before calling replaceCurrentItemWithPlayerItem.

Sadly this involves a voodoo dance with somewhat inconsistent asynchronous KVO, which is no fun.

like image 2
damian Avatar answered Nov 15 '22 21:11

damian