1

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?

4

1 に答える 1

2

これは長いショットかもしれませんが、ドキュメントの次のページを見てください。

AV Foundation プログラミング ガイド - シーク - 再生ヘッドの再配置

私のヒントは、ゼロの許容範囲 (前後) を指定することで、おそらくデコーダーが正確な要求されたフレームを計算するキー フレームから抜け出す原因になるということです。

これは、同時に 2 つのアイテムを再生することと併せて、iOS の内部メディア サービスに割り当てられたリソースを使い果たす可能性があります。したがって、リセットを引き起こします。再生されるビデオの品質とフォーマットが最も重要である多くの要因に依存していると思います.

スクラブで正確な時間制御を必要としない場合 (!)、許容範囲で遊んでみてください。最大キーフレーム間隔より少し大きい値で試すことをお勧めします。

その値 (最大キーフレーム間隔) は、ほとんどの場合、映画に依存します。

ムービーを制御でき、非常識なムービー ファイル サイズに耐えられる場合は、最大キーフレーム間隔 1 (!) を使用してエクスポートし、試してみてください。

于 2013-01-29T02:12:25.337 に答える