我们遇到了类似的问题,并将其追溯到我认为是 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];
}
}
}