2

AVPlayerLooper接受一个模板AVPlayerItem和一个AVQueuePlayer作为设置参数,然后它在内部操作队列的项目,并且播放器不断地改变它的currentItem.

这与 完美配合AVPlayerLayer,它接受这个循环播放器作为参数并只渲染它,但是我如何使用它AVPlayerItemVideoOutput,它被附加到AVPlayerItem播放器内部有多个播放器?我如何在AVPlayerLayer内部重现相同的事情?

AVPlayerLooper来自文档的设置示例

NSString *videoFile = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"mov"];
NSURL *videoURL = [NSURL fileURLWithPath:videoFile];

_playerItem = [AVPlayerItem playerItemWithURL:videoURL];
_player = [AVQueuePlayer queuePlayerWithItems:@[_playerItem]];
_playerLooper = [AVPlayerLooper playerLooperWithPlayer:_player templateItem:_playerItem];
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
_playerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:_playerLayer];
[_player play];

这是AVPlayerItemVideoOutput应该如何使用

[item addOutput:_videoOutput];

我想出的唯一解决方法是观察currentItem每次从旧项目中分离视频输出并将其附加到新项目的变化,如下例所示,但这显然抵消了我试图实现的无缝播放.

- (void)observeValueForKeyPath:(NSString*)path
                      ofObject:(id)object
                        change:(NSDictionary*)change
                       context:(void*)context {
   if (context == currentItemContext) {
      AVPlayerItem* newItem = [change objectForKey:NSKeyValueChangeNewKey];
      AVPlayerItem* oldItem = [change objectForKey:NSKeyValueChangeOldKey];
      if(oldItem.status == AVPlayerItemStatusReadyToPlay) {
          [newItem removeOutput:_videoOutput];
      }
      if(newItem.status == AVPlayerItemStatusReadyToPlay) {
          [newItem addOutput:_videoOutput];
      }
      [self removeItemObservers:oldItem];
      [self addItemObservers:newItem];
   }
}

有关更多上下文,我正在尝试解决颤振的 video_player 插件https://github.com/flutter/flutter/issues/72878

插件的代码可以在这里找到https://github.com/flutter/plugins/blob/172338d02b177353bf517e5826cf6a25b5f0d887/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m

4

1 回答 1

2

您可以根据需要通过子类AVQueuePlayer化 (yay OOP) 并在那里创建和添加AVPlayerItemVideoOutputs 来做到这一点。我以前从未见过 multiple AVPlayerItemVideoOutput,但内存消耗似乎是合理的,并且一切正常。

@interface OutputtingQueuePlayer : AVQueuePlayer
@end

@implementation OutputtingQueuePlayer

- (void)insertItem:(AVPlayerItem *)item afterItem:(nullable AVPlayerItem *)afterItem;
{
    if (item.outputs.count == 0) {
        NSLog(@"Creating AVPlayerItemVideoOutput");
        AVPlayerItemVideoOutput *videoOutput = [[AVPlayerItemVideoOutput alloc] initWithOutputSettings:nil]; // or whatever
        [item addOutput:videoOutput];
    }
    [super insertItem:item afterItem:afterItem];
}
@end

当前输出的访问方式如下:

AVPlayerItemVideoOutput *videoOutput = _player.currentItem.outputs.firstObject;
CVPixelBufferRef pixelBuffer = [videoOutput copyPixelBufferForItemTime:_player.currentTime itemTimeForDisplay:nil];
// do something with pixelBuffer here
CVPixelBufferRelease(pixelBuffer);

配置变为:

_playerItem = [AVPlayerItem playerItemWithURL:videoURL];
_player = [OutputtingQueuePlayer queuePlayerWithItems:@[_playerItem]];
_playerLooper = [AVPlayerLooper playerLooperWithPlayer:_player templateItem:_playerItem];
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
[self.view.layer addSublayer:_playerLayer];
[_player play];
于 2021-01-20T18:39:52.797 回答