Swift: 播放本地视频并实时应用CIFilter

Swift: Play a local video and apply CIFilter in realtime

我正在尝试播放本地视频并实时应用 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
        }
    }
}

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

  • 在组合块外部的某处创建过滤器并保留对它的引用,例如在属性.
  • 此外,只创建一次合成并让它应用 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 如何操作。