将CIFilter应用于video文件并保存

有没有快速,轻量级的方式来将CIFilter应用于video? 在提到之前,我已经看过GPUImage–它看起来像是非常强大的魔法代码,但是对于我正在尝试做的事情来说,这确实是过度杀伤力。

本质上,我想

  1. 拍一个video文件,比如保存在/tmp/myVideoFile.mp4
  2. CIFilter应用于此video文件
  3. 将video文件保存到不同(或相同)的位置,例如/tmp/anotherVideoFile.mp4

我已经能够将CIFilter应用于使用AVPlayerItemVideoOutput

 let player = AVPlayer(playerItem: AVPlayerItem(asset: video)) let output = AVPlayerItemVideoOutput(pixelBufferAttributes: nil) player.currentItem?.addOutput(self.output) player.play() let displayLink = CADisplayLink(target: self, selector: #selector(self.displayLinkDidRefresh(_:))) displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) func displayLinkDidRefresh(link: CADisplayLink){ let itemTime = output.itemTimeForHostTime(CACurrentMediaTime()) if output.hasNewPixelBufferForItemTime(itemTime){ if let pixelBuffer = output.copyPixelBufferForItemTime(itemTime, itemTimeForDisplay: nil){ let image = CIImage(CVPixelBuffer: pixelBuffer) // apply filters to image // display image } } } 

这个工作很好,但是我一直在寻找如何将一个filter应用到一个已经保存的video文件上的麻烦。 有基本上只是做我上面做的,使用AVPlayer ,播放video,并获取像素缓冲区从每一帧,因为它播放,但这将不适用于后台的video处理。 我不认为用户会喜欢只要他们的video是为了应用filter而等待。

在过度简化的代码中,我正在寻找像这样的东西:

 var newVideo = AVMutableAsset() // We'll just pretend like this is a thing var originalVideo = AVAsset(url: NSURL(urlString: "/example/location.mp4")) originalVideo.getAllFrames(){(pixelBuffer: CVPixelBuffer) -> Void in let image = CIImage(CVPixelBuffer: pixelBuffer) .imageByApplyingFilter("Filter", withInputParameters: [:]) newVideo.addFrame(image) } newVideo.exportTo(url: NSURL(urlString: "/this/isAnother/example.mp4")) 

有没有办法快速(再次,不涉及GPUImage,理想的iOS 7的工作方式)应用filter的video文件,然后保存它? 例如,这需要保存的video,将其加载到AVAsset ,应用CIFilter ,然后将新video保存到其他位置。

在iOS 9 / OS X 10.11 / tvOS中,有一种将CIFilter应用于video的便捷方法。 它适用于AVVideoComposition ,因此您可以将它用于播放和文件到文件导入/导出。 对于方法文档,请参阅AVVideoComposition.init(asset:applyingCIFiltersWithHandler:)

苹果公司的Core Image Programming Guide也有一个例子:

 let filter = CIFilter(name: "CIGaussianBlur")! let composition = AVVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in // Clamp to avoid blurring transparent pixels at the image edges let source = request.sourceImage.clampingToExtent() filter.setValue(source, forKey: kCIInputImageKey) // Vary filter parameters based on video timing let seconds = CMTimeGetSeconds(request.compositionTime) filter.setValue(seconds * 10.0, forKey: kCIInputRadiusKey) // Crop the blurred output to the bounds of the original image let output = filter.outputImage!.cropping(to: request.sourceImage.extent) // Provide the filter output to the composition request.finish(with: output, context: nil) }) 

那部分设定了构成。 完成之后,您可以通过将其分配给AVPlayer进行播放,或使用AVAssetExportSession将其写入文件。 既然你是追随后者,下面是一个例子:

 let export = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset1920x1200) export.outputFileType = AVFileTypeQuickTimeMovie export.outputURL = outURL export.videoComposition = composition export.exportAsynchronouslyWithCompletionHandler(/*...*/) 

关于Core Image的WWDC15会议还有更多的关于这个问题的讨论,大约在20分钟左右 。


如果你想要一个适用于早期操作系统的解决scheme,那就更复杂了。

另外:想想你真的需要支持多久。 截至2016年8月15日 ,87%的设备位于iOS 9.0或更高版本,97%位于iOS 8.0或更高版本。 要付出很大的努力来支持潜在客户群中的一小部分 – 在您完成项目并准备部署时,它会变得更小 – 可能不值得花费。

有几种方法可以去做。 无论哪种方式,您都将获得代表源帧的CVPixelBuffer , 从中创buildCIImage ,应用滤镜以及渲染新的CVPixelBuffer

  1. 使用AVAssetReaderAVAssetWriter读取和写入像素缓冲区。 在Apple的“AVFoundation编程指南”的“ 导出”一章中,有一些如何做到这一点的例子(读写部分;您仍然需要在两者之间进行过滤)。

  2. 使用AVVideoComposition与一个自定义的AVVideoComposition类。 您的自定义合成器被赋予AVAsynchronousVideoCompositionRequest对象,这些对象提供对像素缓冲区的访问,并为您提供处理后的像素缓冲区。 苹果有一个名为AVCustomEdit的示例代码项目,演示了如何做到这一点(再次,只是获取和返回示例缓冲区的一部分;你想要使用核心图像,而不是使用他们的GL渲染器)。

在这两者中, AVVideoComposition路由更加灵活,因为您可以使用合成播放和导出。