Я написал фильтр хромакея, чтобы сделать фон фильмов MPEG прозрачным, чтобы вы могли использовать файл фильма для более длинных анимаций без необходимости длинных последовательностей PNG (как это обычно делается для некоторых типов анимации iOS).
Я использую AVPlayer
, AVVideoComposition
и пользовательский CIFilter
для рендеринга видео поверх фонового изображения. Фоновое изображение может динамически изменяться пользователем, взаимодействующим с приложением.
Раньше это работало нормально, пока не вышла iOS 10, а теперь не работает.
Что происходит сейчас, так это то, что видео воспроизводится, но хроматический ключ не происходит, и Xcode постоянно выдает следующую ошибку:
need a swizzler so that YCC420v can be written.
Вот изображение того, что должен произвести CIFilter
:
И вместо этого это то, что он производит (начиная с iOS 10):
Вот раздел моего кода, который создает EAGLContext
и применяет пользовательский CIFilter
:
let myEAGLContext = EAGLContext.init(API: EAGLRenderingAPI.OpenGLES2)
//let cicontext = CIContext.init(EAGLContext: myEAGLContext, options: [kCIContextWorkingColorSpace: NSNull()])
let cicontext = CIContext.init(EAGLContext: myEAGLContext)
let filter = ChromaKeyFilter()
filter.activeColor = CIColor.init(red: 0, green:1.0, blue: 0.0)
filter.threshold = self.threshold
//most of below comes from the "WWDC15 What's New In Core Image" slides
let vidComp = AVVideoComposition(asset: videoAsset!,
applyingCIFiltersWithHandler:
{
request in
let input = request.sourceImage.imageByClampingToExtent()
filter.inputImage = input
let output = filter.outputImage!.imageByClampingToExtent()
request.finishWithImage(output, context: cicontext)
self.reloadInputViews()
})
let playerItem = AVPlayerItem(asset: videoAsset!)
playerItem.videoComposition = vidComp
self.player = AVPlayer(playerItem: playerItem)
self.playerInitialized = true
let layer = AVPlayerLayer(player: player)
self.subviews.forEach { subview in
subview.removeFromSuperview()
}
layer.frame = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: self.frame.size.height)
self.layer.addSublayer(layer)
А вот код для пользовательского CIFilter
:
private class ChromaKeyFilter : CIFilter {
private var kernel: CIColorKernel!
var inputImage: CIImage?
var activeColor = CIColor(red: 0.0, green: 1.0, blue: 0.0)
var threshold: Float = 0.05
override init() {
super.init()
kernel = createKernel()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
kernel = createKernel()
}
override var outputImage: CIImage? {
if let inputImage = inputImage {
let dod = inputImage.extent
let args = [inputImage as AnyObject, activeColor as AnyObject, threshold as AnyObject]
return kernel.applyWithExtent(dod, arguments: args)
}
return nil
}
private func createKernel() -> CIColorKernel {
let kernelString =
"kernel vec4 chromaKey( __sample s, __color c, float threshold ) { \n" +
//below kernel was adapted from the GPUImage custom chromakeyfilter:
//https://github.com/BradLarson/GPUImage/blob/master/framework/Source/GPUImageChromaKeyFilter.m#L30
" float maskY = 0.2989 * c.r + 0.5866 * c.g + 0.1145 * c.b;\n" +
" float maskCr = 0.7132 * (c.r - maskY);\n" +
" float maskCb = 0.5647 * (c.b - maskY);\n" +
" float Y = 0.2989 * s.rgb.r + 0.5866 * s.rgb.g + 0.1145 * s.rgb.b;\n" +
" float Cr = 0.7132 * (s.rgb.r - Y);\n" +
" float Cb = 0.5647 * (s.rgba.b - Y);\n" +
" float blendValue = smoothstep(threshold, threshold + 0.5, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));\n" +
" return blendValue * vec4( s.rgb, 1.0 ); \n" +
"}"
let kernel = CIColorKernel(string: kernelString)
return kernel!
}
}
У кого-нибудь есть идеи о том, почему это только сейчас ломается? Интересно, ломается только на телефоне. Он по-прежнему работает в симуляторе, хотя и намного медленнее, чем до выхода iOS 10.