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