[iOS]AVPlayerItemVideoOutput.hasNewPixelBufferForItemTime无法正常工作

11

这是我在这里的第一个问题,请不要太严厉。

我正在使用AVPlayer从网络播放视频。 我使用附加到AVPlayerItem的AVPlayerItemVideoOutput输出当前帧。为了检查新帧是否已准备好,我调用[AVPlayerItemVideoOutput hasNewPixelBufferForItemTime],然后使用OpenGL ES进行输出。 如果我读取mp4,一切都完美无缺,但如果我尝试读取m3u8,则仅在大约1秒钟(~ 30帧)的时间内工作,但此后[AVPlayerItemVideoOutput hasNewPixelBufferForItemTime]始终返回假值,因此当前帧未更新。

如果在出现此问题之前使用[AVPlayer seekToTime]寻找当前帧,所有操作都正常。

我使用的测试m3u8视频位于此处:

http://195.16.112.71/adaptive/3006a26a-9154-4b38-a327-4fa2a2381ae6.video/3006a26a-9154-4b38-a327-4fa2a2381ae6.m3u8

为了重现此问题,我修改了苹果的示例AVPlayerDemo,以下是它:https://yadi.sk/d/T2aVGoKnWmf5Z

主要更改在于我调用了[AVPlayerDemoPlaybackViewController update],它调用了上述的[AVPlayerItemVideoOutput hasNewPixelBufferForItemTime]。该函数具有静态变量counter,用于存储成功调用[AVPlayerItemVideoOutput copyPixelBufferForItemTime]的次数。

视频网址在[AVPlayerDemoPlaybackViewController setURL]中设置,它在函数开头是硬编码的。默认情况下,其值指向m3u8视频,这会导致问题出现,在这种情况下,counter的平均值约为30,在帧索引达到该值后,[AVPlayerItemVideoOutput hasNewPixelBufferForItemTime]仅返回FALSE。

在使用其他视频网址时(请参见[AVPlayerDemoPlaybackViewController setURL]的开头 - 有一个可取消注释的备用网址),所有帧都可以成功读取。

将不胜感激地接受任何帮助!

3个回答

5

以下代码并没有解决我的问题,从 [AVPlayerItemVideoOutput hasNewPixelBufferForItemTime] 我仍然得不到任何东西。

if (failedCount > 100) {
    failedCount = 0;
    [_playerItem removeOutput:_output];
    [_playerItem addOutput:_output];
}

经过一整天的测试,我终于找到了解决方法。

#pragma mark - AVPlayerItemOutputPullDelegate
- (void)outputMediaDataWillChange:(AVPlayerItemOutput *)sender {
    if (![self.videoOutput hasNewPixelBufferForItemTime:CMTimeMake(1, 10)]) {
        [self configVideoOutput];
    }
    [self.displayLink setPaused:NO];
}

在调用outputMediaDataWillChange:方法时,请检查[AVPlayerItemVideoOutput hasNewPixelBufferForItemTime]。如果0.1秒内没有新的像素缓冲区,则重新创建AVPlayerItemVideoOutput

[self configVideoOutput];代码中,只需重新创建一个新的AVPlayerItemVideoOutput来替换当前的videoOutput属性。

为什么是0.1秒?

我进行了多次测试和实验,发现前1或2帧可能总是没有像素缓冲区。因此,在视频播放开始的1/30秒或者2/30秒(对于30fps的视频)可能没有帧和像素缓冲区。但是,如果0.1秒后仍然没有视频像素缓冲区,则视频输出可能会出现问题。所以我们需要重新创建它。


1
这对我有用。从AVPlayerItem中删除并添加新输出似乎可以解决问题。 - Kazzin
这也解决了我的问题。非常感谢,在找到这篇帖子之前,我花了一段时间来解决这个问题。 - crgt
我还需要重新创建输出,然后再添加回去才能让我的工作正常。 - Clay Garrett
我希望我能给你多个赞。这个解决方案也对我有用。谢谢,老兄! - Eugene Alexeev

3
在调用AVPlayerItem- (void)addOutput:(AVPlayerItemOutput *)output方法之前,请确保AVPlayerItem.status等于AVPlayerItemStatusReadyToPlay

参考:此页面上Renaud的回复

我的实现也遇到了相同的问题。在尝试了这里提出的解决方案后,我最终找到了可靠的方法

AVPlayerItemVideoOutput必须在AVPlayerItem状态准备好播放之后创建。

因此:

  1. 创建播放器和播放器项目、派发队列和显示链接
  2. AVPlayerItem状态密钥注册观察者
  3. 在状态AVPlayerStatusReadyToPlay下,创建AVPlayerItemVideoOutput并启动显示链接

感谢大家的启示

Renaud


2
我发现,在使用HLS多比特率播放列表时,AVPlayerItemVideoOutput出现了“卡顿”的情况。当播放器切换到较高的比特率时-> playeritems视频轨道的trackid会改变->它将得到几个像素缓冲区,但之后hasNewPixelBufferForItemTime总是返回NO。
我已经花了几天时间来解决这个问题。无意中我注意到,如果我转到后台然后再回到前台->视频将以更高的比特率正常播放。但这不是解决方案。
最终,我找到了解决这个问题的方法。我为失败的像素缓冲区设置了计数器,当失败次数达到100次时,我从playeritem中删除当前输出并重新设置相同的实例。
if (failedCount > 100)
    {
        failedCount = 0;
        [_playerItem removeOutput:_output];
        [_playerItem addOutput:_output];
    }

这对我很管用,谢谢。但我认为最好使用计时器而不是计数器。我发现在视频停顿1秒后可以使用此方法。 - Paltr
这对我也起作用了。可惜似乎没有提供API的方式来解决这个问题。 - moka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接