1

跟随问题。我有两个 AVPlayer,每个都用不同的 AVPlayerItem 初始化。成功加载 AVPlayerItem 后,我将 AVPlayerLayer 添加到视图层。正如您在下面的代码中看到的那样。

- (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");
        }
    }
}
}

当我点击播放按钮时,视频就会播放。如果我再次击中它,它们就会停下来,到目前为止一切都很好。

- (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];
    }
}

我有两个 UISlider 可以让我来回浏览每个视频。我使用 seekToTime 跳转到视频中的某个时间。

- (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];
    }
}

视频也可以有不同的长度。但是,如果视频结束,我将它设置回开始,同样使用 seekToTime。

我的问题是我最初可以播放两个视频。我可以毫无问题地暂停和恢复它们。但是,一旦我暂停并对任何视频使用 seekToTime 并继续播放,或者如果视频结束,我会使用 seekToTime 重置它们并再次点击播放,我的状态观察器会为两个 PlayerItems 触发 AVPlayerItemStatusFailed。

然后这些项目的持续时间变为 0。我检查过,PlayerItems 不是零。

我没有崩溃,但我不能再播放视频了。当我只使用一个播放器时,我可以使用 seekToTime 跳过它,也可以在视频结束时将其重置,没有问题。

我通读了论坛,人们说您显然最多可以使用 4 个 AVPlayer,所以我想我应该保存 2 个。我还读到其他应用程序可以用完视频渲染管道,但我确保我没有在我的 iPad 上的后台有任何其他应用程序。

非常感谢任何有关为什么这对我不起作用甚至更好的解决方法的帮助。

更新: 我现在实际上从 AVPlayerItem 对象记录了错误:

error: Error Domain=AVFoundationErrorDomain Code=-11819 "Cannot Complete Action"  
UserInfo=0x19e370 {NSLocalizedRecoverySuggestion=Try again later., 
NSLocalizedDescription=Cannot Complete Action}

代码 -11819 代表 AVErrorMediaServicesWereReset。这对任何人帮助我有帮助吗?

4

1 回答 1

2

这可能是一个很长的镜头,但请查看文档的以下页面:

AV Foundation 编程指南 - Seeking—重新定位播放头

我的提示是,通过将容差设为零(之前和之后),您可能会导致解码器从计算确切请求帧的关键帧中解脱出来。

这与同时播放两个项目一起,可能导致分配给 iOS 内部媒体服务的资源耗尽。从而导致复位。我想这取决于许多因素,其中最重要的是正在播放的视频的质量和格式。

如果您不需要在擦洗(!)中进行精确的时间控制,请尝试使用公差。我建议您尝试使用略大于最大关键帧间隔的值。

该值(最大关键帧间隔)很可能取决于电影。

如果您可以控制电影并且可以忍受疯狂的电影文件大小,请尝试使用最大关键帧间隔 1 (!) 导出它们并试一试。

于 2013-01-29T02:12:25.337 回答