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



我的视频播放器遇到了一个奇怪的情况,它的核心代码与我早期制作的应用程序没有太大变化。问题是:我插入了一个"_loadingLayer"(一个表示视频正在加载的CATextLayer),然后观察AVPlayer的currentItem的status属性,以确定何时删除"_loading Layer"并将其替换为我实际的"_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",但播放器层实际上并没有准备好显示。这对我来说毫无意义——这似乎违反了直觉。有人能给我一些指导吗?

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

我认为还有一件奇怪的事情可能与此有关。。。。我在调试器控制台中看到了以下消息:

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

提前谢谢。这是来自苹果开发者论坛的交叉帖子。

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

如果AVPlayerItem在获得AVPlayerLayer之前报告了AVPlayerStatusReadyToPlay的状态,则AVPlayer将永远不会报告它已准备好显示。

所以当你这样做的时候:

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之前。

以下是使用苹果测试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];
}
}
}

最新更新