将CIFilter应用于video文件并保存
有没有快速,轻量级的方式来将CIFilter
应用于video? 在提到之前,我已经看过GPUImage–它看起来像是非常强大的魔法代码,但是对于我正在尝试做的事情来说,这确实是过度杀伤力。
本质上,我想
- 拍一个video文件,比如保存在
/tmp/myVideoFile.mp4
- 将
CIFilter
应用于此video文件 - 将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
。
-
使用
AVAssetReader
和AVAssetWriter
读取和写入像素缓冲区。 在Apple的“AVFoundation编程指南”的“ 导出”一章中,有一些如何做到这一点的例子(读写部分;您仍然需要在两者之间进行过滤)。 -
使用
AVVideoComposition
与一个自定义的AVVideoComposition
类。 您的自定义合成器被赋予AVAsynchronousVideoCompositionRequest
对象,这些对象提供对像素缓冲区的访问,并为您提供处理后的像素缓冲区。 苹果有一个名为AVCustomEdit的示例代码项目,演示了如何做到这一点(再次,只是获取和返回示例缓冲区的一部分;你想要使用核心图像,而不是使用他们的GL渲染器)。
在这两者中, AVVideoComposition
路由更加灵活,因为您可以使用合成播放和导出。
- __connection_block_invoke_2中的错误:iPad上的连接中断
- 如何将Decimal转换为分隔符后两位数的string?
- Swift – 如何知道循环何时结束?
- 如何使用getData()在Swift中绘制带有ObjC PNChart库的LineChart?
- 在Swift中从相机裁剪图像,而不移动到另一个ViewController
- 如何更改导航栏的后退button的字体?
- 如何在swift中创buildUnsafeMutablePointer <CGPoint>对象
- Swift 3 Firebase更新代码转换 – sorting
- 如何在swift中将数组保存到App的mainBundle中.plist