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 .. ?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With