AV Foundation:currentItem准备播放和 - [AVPlayer readyForDisplay]属性之间的区别?

sti*_*ggs 5 macos cocoa avfoundation

我的视频播放器遇到了一个奇怪的情况,其核心代码与我之前制作的应用程序中的功能没有太大变化.这是问题所在:我正在插入一个"_loadingLayer"(一个说明视频正在加载的CATextLayer),然后观察AVPlayer的currentItem的status属性以确定何时删除"_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];

}

}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

提前致谢.这是Apple Dev论坛的一个交叉点.

rhh*_*rhh 5

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

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

所以当你这样做时:

self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
Run Code Online (Sandbox Code Playgroud)

确保后面跟着:

self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; 
Run Code Online (Sandbox Code Playgroud)

并且如果两者之间有任何代码,则您没有太多代码。

我建立了一个测试台,让它在 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];
    }
  }
}
Run Code Online (Sandbox Code Playgroud)