Following problem. I have two AVPlayers, each initialized with a different AVPlayerItem. Once the AVPlayerItem is successfully loaded, I add the AVPlayerLayer to the view layer. As you can see in the code below.
- (void)viewDidLoad
{
[super viewDidLoad];
self.playerItem = [[AVPlayerItem alloc] initWithURL:[self.video getVideoPath]];
[self.playerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
[self.playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:0 context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(videoEnded)
name:AVPlayerItemDidPlayToEndTimeNotification
object:nil];
self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
self.overlayPlayerItem = [[AVPlayerItem alloc] initWithURL:[self.compareVideo getVideoPath]];
[self.overlayPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
[self.overlayPlayerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:0 context:nil];
if (self.overlayPlayer==nil) {
self.overlayPlayer = [[AVPlayer alloc] initWithPlayerItem:self.overlayPlayerItem];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([object isKindOfClass:[AVPlayerItem class]])
{
AVPlayerItem *item = (AVPlayerItem *)object;
//playerItem status value changed?
if ([keyPath isEqualToString:@"status"])
{ //yes->check it...
switch(item.status)
{
case AVPlayerItemStatusFailed:
NSLog(@"player item status failed");
break;
case AVPlayerItemStatusReadyToPlay:
{
NSLog(@"player item status is ready to play");
if (item == self.playerItem && !videoLayerAdded) {
videoLayerAdded = YES;
AVPlayerLayer* layer = [AVPlayerLayer playerLayerWithPlayer:player];
layer.frame = self.videoContainer.bounds;
[self.videoContainer.layer insertSublayer:layer atIndex:0];
} else if (item == self.overlayPlayerItem && !overlayLayerAdded){
overlayLayerAdded = YES;
AVPlayerLayer* layer = [AVPlayerLayer playerLayerWithPlayer:overlayPlayer];
layer.frame = self.videoOverlayContainer.bounds;
[self.videoOverlayContainer.layer insertSublayer:layer atIndex:0];
}
break;
}
case AVPlayerItemStatusUnknown:
NSLog(@"player item status is unknown");
break;
}
}
else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
{
if (item.playbackBufferEmpty)
{
NSLog(@"player item playback buffer is empty");
}
}
}
}
When I hit the play button the videos get played. If I hit it again they stop, so far so good.
- (IBAction)playButtonClicked:(id)sender{
//is playing
if(self.player.rate>0.0 || self.overlayPlayer.rate>0.0){
[self.player pause];
[self.overlayPlayer pause];
} else {
[self.player play];
[self.overlayPlayer play];
}
}
I have two UISlider that allow me to go forth and back through each video. I use seekToTime to jump to a certain time in a video.
- (IBAction)sliderChanged:(id)sender{
UISlider *slider = (UISlider *)sender;
//stop any video when using the slider
if(self.player.rate>0.0){
[self.player pause];
}
if(self.overlayPlayer.rate>0.0){
[self.overlayPlayer pause];
}
if (slider.tag == 1) {
double time = (self.playerTimeSlider.value * CMTimeGetSeconds(self.player.currentItem.asset.duration));
[self.player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
} else if (slider.tag == 2){
double time = (self.overlayTimeSlider.value * CMTimeGetSeconds(self.overlayPlayer.currentItem.asset.duration));
[self.overlayPlayer seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}
}
Videos can have different length as well. However, if a video ends, I set it back to the start, also with seekToTime.
My problem is that I can initially play both videos. I can pause and resume them without problem. But once I pause and use seekToTime for any video and resume or if the videos are at the end I reset them with seekToTime and hit play again my status observer fires AVPlayerItemStatusFailed for both PlayerItems.
Then the duration of those items becomes 0. I checked though, the PlayerItems are not nil.
I don't get a crash but I just can't play the videos anymore. When I only use one player I can jump through it with seekToTime and also reset it at the end of the video without problem.
I read through the forum and people say you can apparently use up to 4 AVPlayers, so I guess I should be save with 2. I also read about that other apps can use up video render pipelines, but I made sure that I don't have any other apps in the background on my iPad.
Any help as to why this is not working for me or even better, a fix, is highly appreciated.
Update: I actually logged the error from the AVPlayerItem object now:
error: Error Domain=AVFoundationErrorDomain Code=-11819 "Cannot Complete Action"
UserInfo=0x19e370 {NSLocalizedRecoverySuggestion=Try again later.,
NSLocalizedDescription=Cannot Complete Action}
The code -11819 stands for AVErrorMediaServicesWereReset. Does that help anyone to help me?