5

我的视频播放器遇到了一个奇怪的情况,其核心代码与我早期制作的应用程序中的代码没有太大变化。这就是问题所在:我正在插入一个“_loadingLayer”(一个表示视频正在加载的 CATextLayer),然后观察 AVPlayer 的 currentItem 的状态属性以确定何时删除“_loadingLayer”并将其替换为我的实际“_playerLayer” . 这是我的 KVO 代码:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

if ((object == _playerLayer) && (_playerLayer.player.currentItem.status == AVPlayerItemStatusReadyToPlay)) {

    [CATransaction setAnimationDuration:1.8];

    _loadingLayer.opaque = NO;

    if (_playerLayer.readyForDisplay) {

        NSLog(@"Should be ready now.");

    }

    [self addPlayerLayerToLayerTree];

}

}

我的问题是视频正在开始,但只有音频正在播放——图层保持黑色。当我插入上面的 NSLog 语句时,我发现了原因: 显然,虽然 currentItem 的状态是“AVPlayerItemStatusReadyToPlay”,但播放器层实际上并不是 readyForDisplay。这对我来说毫无意义——这似乎违反直觉。有人可以给我一些指导吗?

通过将背景颜色设置为红色,我能够验证 _playerLayer 是否已添加到图层树中。

我认为可能与之相关的另一件奇怪的事情......我一直在调试器控制台中看到这些消息:

PSsetwindowlevel, 错误设置窗口级别(1000) CGSSetIgnoresCycle: error 1000 设置或清除窗口标签

提前致谢。这是来自 Apple 开发论坛的交叉帖子。

4

1 回答 1

5

我们遇到了类似的问题,并将其追溯到我认为是 iOS 5.1(可能还有早期版本)中的错误。它已在 iOS 6.0 中修复。由于我无法在任何地方找到解决方案,因此我正在为未来遇到此问题的人写一篇长篇文章。

如果 AVPlayerItem 在获得 AVPlayerLayer 之前报告了 AVPlayerStatusReadyToPlay 的状态,那么 AVPlayer 将永远不会报告它是 readyForDisplay。

所以当你这样做时:

self.player = [AVPlayer playerWithPlayerItem:self.playerItem];

确保紧随其后:

self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; 

并且您在两者之间没有太多代码。

我构建了一个测试台,让它 100% 的时间工作或 100% 的失败。请注意,要查看实际应用程序中发生的情况可能会很棘手,因为您在视频上会有不同的加载时间,这会影响 playerItem 报告 AVPlayerStatusReadyToPlay 的速度。

如果您想在您的应用程序中进行测试,请将其放入一个简单的视图中。以下将在 iOS 5.1 上不起作用(即您将听到音频但看不到视频)。如果您将 loadPlayerLayer 切换为在 loadPlayer 结束时调用,它将始终有效。

未来读者的后续:一些玩家事件可以改变这个顺序,让你认为它是有效的。不过,它们是红鲱鱼,因为它们无意中颠倒了加载顺序,使得 playerLayer 在 AVStatusReadyToPlay 之前被抓取。这些事件是:寻找视频、转到主屏幕然后重新激活应用程序、播放器切换到 HLS 视频中的不同视频/音频轨道。这些动作再次触发 AVStatusReadyToPlay,从而使 playerLayer 在 AVStatusReadyToPlay 之前发生。

这是使用 Apple 的测试 HLS 视频的测试工具:

-(void)loadPlayer
{
  NSLog(@"loadPlayer invoked");

  NSURL *url = [NSURL URLWithString:@"https://devimages.apple.com.edgekey.net/resources/http-streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"];
  self.playerItem = [AVPlayerItem playerItemWithURL:url];
  [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:&kPlayerContext];
  self.player = [AVPlayer playerWithPlayerItem:self.playerItem];

}

-(void)loadPlayerLayer
{
  NSLog(@"starting player layer");
  self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; 
  [self.playerLayer addObserver:self forKeyPath:@"readyForDisplay" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:&kPlayerLayerContext];
  [self.playerLayer setFrame:[[self view] bounds]];
  [[[self view] layer] addSublayer:self.playerLayer];
}

-(void)observeValueForKeyPath:(NSString*)path ofObject:(id)object change:(NSDictionary*)change context:(void*) context
{
  if(context == &kPlayerContext){
    if([self.player status] == AVPlayerStatusReadyToPlay){
      NSLog(@"Player is ready to play");
      //Robert: Never works if after AVPlayerItem reports AVPlayerStatusReadyToPlay
      if(!self.startedPlayerLayer){
        self.startedPlayerLayer = YES;
        [self loadPlayerLayer];
      }
    }
  }

  if(context == &kPlayerLayerContext){
    if([self.playerLayer isReadyForDisplay] == YES){
      NSLog(@"PlayerLayer says it's ready to display now");
      [self playTheVideoIfReady];
    }
  }
}
于 2012-10-28T21:39:42.317 回答