【问题标题】:Swift: Play a local video and apply CIFilter in realtimeSwift:播放本地视频并实时应用 CIFilter
【发布时间】:2021-06-14 03:31:10
【问题描述】:

我正在尝试播放本地视频并实时应用CIFilter,没有延迟。我怎样才能做到这一点?我已经知道如何将CIFilter 应用到AVPlayer 中的视频,但它的性能并没有我想要的那么快。

这是我当前的代码:

@objc func exposure(slider: UISlider, event: UIEvent) {
    if let touchEvent = event.allTouches?.first {
        switch touchEvent.phase {
        case .moved:
            player.currentItem?.videoComposition = AVVideoComposition(asset: player.currentItem!.asset, applyingCIFiltersWithHandler: { request in
             
                let exposureFilter = CIFilter.exposureAdjust()
                exposureFilter.inputImage = request.sourceImage.clampedToExtent()
                exposureFilter.ev = slider.value
             
                let output = self.exposureFilter.outputImage!.cropped(to: request.sourceImage.extent)

                // Provide the filter output to the composition
                request.finish(with: output, context: nil)
         })
        default:
            break
        }
    }
}

【问题讨论】:

  • 欢迎您!您能描述一下您使用的是哪种过滤器吗?根据过滤器的不同,过滤操作本身可能非常昂贵,导致播放期间的 FPS 较低。
  • 嗨弗兰克!我正在应用曝光、对比度、温度等基本滤镜。这些滤镜是通过移动滑块来应用的,这就是我需要实时性能的原因。
  • 我明白了。您能否发布一些代码来显示您如何应用过滤器以及更改滑块如何更改过滤器值?谢谢!
  • 完成@FrankSchlegel
  • @FrankSchlegel 如何解决播放过程中 FPS 少的问题?

标签: swift metal cifilter


【解决方案1】:

问题是您重新创建视频合成并将其重新分配给播放器项目每次滑块值更改。这是非常昂贵且不必要的。您可以改为执行以下操作:

  • 在组合块外部的某处创建过滤器并保留对它的引用,例如在属性中。
  • 另外,只创建一次合成并让它应用 referenced 过滤器(而不是每帧都创建一个新的)。
  • 当滑块值发生变化时,只需设置过滤器对应的参数值即可。下次合成将渲染一帧时,它将自动使用新参数值,因为它使用对刚刚更改的滤镜的引用。

类似这样的:

let exposureFilter = CIFilter.exposureAdjust()

init() {
    // set initial composition
    self.updateComposition()
}

func updateComposition() {
    player.currentItem?.videoComposition = AVVideoComposition(asset: player.currentItem!.asset, applyingCIFiltersWithHandler: { request in
        self.exposureFilter.inputImage = request.sourceImage.clampedToExtent()
        let output = self.exposureFilter.outputImage!.cropped(to: request.sourceImage.extent)
        request.finish(with: output, context: nil)
    })
}

@objc func exposureChanged(slider: UISlider) {
    self.exposureFilter.ev = slider.value
    // we need to re-set the composition if the player is paused to cause an update (see remark below)
    if player.rate == 0.0 {
        self.updateComposition()
    }
}

(顺便说一句,当滑块值发生变化时,您只需执行slider.addTarget(self, action:#selector(exposureChanged(slider:)), for: .valueChanged) 即可获得通知。无需评估事件。)

最后一点:当您想要重新分配合成时,实际上有一个用例,即播放器当前处于暂停状态,但您仍希望显示当前帧的预览并更改过滤器值。具体操作方法请参考this technical note from Apple

【讨论】:

  • 这非常有效。我的意思是,性能很快,但不是非常快。你不觉得我应该用 Metal 来达到那个速度吗?
  • 这很奇怪。 exposureAdjust 绝对应该能够以非常高的帧速率过滤视频帧。你确定是过滤器的问题?您是否尝试过在合成中什么都不做(只是传递输入帧)并比较性能?
  • 是的,它以非常高的帧速率过滤视频帧(这就是我将您的答案标记为正确的原因),但我认为 Metal 可以做得更快。无论如何,我会按照您告诉我的方式进行,非常感谢您的回答!但现在我面临另一个问题:如果我在 AVPlayer 暂停时移动滑块,我无法实时看到更改,当我再次点击播放时我会看到它们。你知道怎么解决吗?
  • 是的,请参阅我的回答中的“最后一点”。
  • 哦,对不起我的无知。我阅读了该技术说明,但我并不真正理解它...您能否告诉我应该如何将新代码集成到您的答案中的代码中?
猜你喜欢
  • 2014-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多