can someone help me to understand the correct way to switch CIFilters without restart video player?
I have a local video playing inside a view. If I tap a cell in collection view, video will change the CIFilter.
my code
let filter = CIFilter(name: "CIPhotoEffectNoir")!
let asset = AVAsset(url: fooURL)
let item = AVPlayerItem(asset: asset)
item.videoComposition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in
let source = request.sourceImage.clampedToExtent()
filter.setValue(source, forKey: kCIInputImageKey)
let output = filter.outputImage
request.finish(with: output!, context: nil)
})
player = AVPlayer(playerItem: item)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = videoViewDetail.bounds
videoViewDetail.layer.addSublayer(playerLayer)
player?.play()
code works great and effect is correct apply
but i have a collectionview with many CIFilters. On tap to a cell i cannot find the way to switch the filter associated to video. If i recreate a new player with the new filter and I add it as substitute to the current "addsublayer" the player will restart video.
@IBAction func tap(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: self.collectionView)
let indexPath = self.collectionView.indexPathForItem(at: location)
if let index = indexPath {
// code to switch to another CIFilter like for example "CISepiaTone"
}
}
What is the best way to change CIFilters to the playing video without restart the video Is it possible to save the video with the new filter?
Thanks!
The handler block that you supply when creating the AVVideoComposition
is continuously called for every video frame the player is showing. That means you just have to switch the filter that is used inside that block.
The easiest way to achieve that is to reference a filter from outside the block instead of capturing it. Then you can just change the reference at runtime.
For example, assuming your code is run inside some view controller method, you can do the following:
class MyViewController: UIViewController {
var filter: CIFilter = CIFilter(name: "CIPhotoEffectNoir")!
func createPlayer() {
let asset = AVAsset(url: fooURL)
let item = AVPlayerItem(asset: asset)
item.videoComposition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { [weak self] request in
guard let self = self else {
request.finish(with error: SomeError)
return
}
let source = request.sourceImage.clampedToExtent()
self.filter.setValue(source, forKey: kCIInputImageKey)
let output = self.filter.outputImage
request.finish(with: output!, context: nil)
})
player = AVPlayer(playerItem: item)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = videoViewDetail.bounds
videoViewDetail.layer.addSublayer(playerLayer)
player?.play()
}
// ...
}
Then you can easily change the filter by just changing self.filter
.
(You may want to synchronize access to the filter though, to avoid concurrency issues.)