8

我正在使用 Swift 在视图的 AVPlayerLayer 中显示来自 AVPlayer 的内容。关联的 AVPlayerItem 有一个 videoComposition,创建它的代码略微简化版本(没有错误检查等)如下所示:

playerItem.videoComposition = AVVideoComposition(asset: someAsset, applyingCIFiltersWithHandler: {
                [unowned self] (request: AVAsynchronousCIImageFilteringRequest) in

    let paramDict = << set up parameter dictionary based on class vars >>

    // filter the image
    let filter = self.ciFilterWithParamDict(paramDict) {
    filter.setValue(request.sourceImage, forKey: kCIInputImageKey)
    if let filteredImage = filter.outputImage {
        request.finishWithImage(filteredImage, context: nil)
    }
})

当 AVPlayer 正在播放或搜索时,这一切都按预期工作。如果创建并加载了新的 videoComposition,则 AVPlayerLayer 会正确渲染。

但是,当我更改了一些用于计算过滤器参数的值时,我还没有找到一种方法来“触发” AVPlayer/AVPlayerItem/AVVideoComposition 以重新渲染。如果我更改值然后播放或搜索,它会正确呈现,但前提是我播放或搜索。有没有办法“就地”触发渲染?

4

5 回答 5

6

我知道的最佳方法是在暂停的 AVPlayer 上编辑 CIFilter 输入时为 AVPlayerItem 创建一个新的 AVVideoComposition 实例。根据我的经验,它比将播放器物品换回播放器更快更干净。您可能认为创建新的视频合成很慢,但实际上您所做的只是在特定帧重新定义渲染路径,这几乎与使受更改影响的 Core Image 缓存部分无效一样有效。

这里的关键是播放器项目的视频合成必须以某种方式失效才能触发重绘。遗憾的是,简单地更改核心图像过滤器的输入参数(据我所知)无法使视频合成无效,这是问题的根源。

您可以通过为 AVPlayerItem 创建 AVMutableVideoComposition 实例并在暂停编辑时以某种方式(通过更改指令、animationTool、frameDuration 等内容)对其进行变异来提高效率。

于 2017-08-17T05:25:13.743 回答
4

我使用 hack 完全替换了 avPlayerItem 以强制刷新。但是如果有一种方法可以直接触发 avPlayerItem 重新渲染,那就更好了。

// If the video is paused, force the player to re-render the frame.
if (self.avPlayer.currentItem.asset && self.avPlayer.rate == 0) {
    CMTime currentTime = self.avPlayerItem.currentTime;

    [self.avPlayer replaceCurrentItemWithPlayerItem:nil];
    [self.avPlayer replaceCurrentItemWithPlayerItem:self.avPlayerItem];
    [self.avPlayerItem seekToTime:currentTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}
于 2016-06-10T17:48:37.340 回答
2

按照这个答案的思路,但不是创建一个全新的AVVideoComposition并将其设置为玩家的videoComposition,看来您可以通过简单地设置videoCompositionnil然后立即返回到现有videoComposition实例来强制刷新当前帧。

每当您想要强制刷新当前帧时,这都会导致以下简单的解决方法:

let videoComposition = player.currentItem?.videoComposition
player.currentItem?.videoComposition = nil
player.currentItem?.videoComposition = videoComposition
于 2018-07-23T01:50:32.710 回答
1

这个可能对你有帮助。

let currentTime = self.player.current()
self.player.play()
self.player.pause()
self.player.seek(to: currentTime, toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero)
于 2017-05-16T06:25:49.407 回答
0

这个在我的情况下有效:

let playerItem = player.currentItem!
pausePlayback()
playerItem.videoComposition = nil
playerItem.videoComposition = AVVideoComposition(asset: playerItem.asset) { request in
        let source = request.sourceImage.clampedToExtent()
        filter?.setValue(source, forKey: kCIInputImageKey) //Any CIFilter
        request.finish(with: filter?.outputImage!, context: nil)
}
resumePlayback()
于 2021-10-12T08:21:33.073 回答