将CIFilter应用于视频文件并保存

时间:2016-08-24 05:10:44

标签: ios swift cifilter avasset

是否有任何快速,轻量级的方式将CIFilter应用于视频?在它提到之前,我已经看了GPUImage - 它看起来像是非常强大的 magic 代码,但它真的有点过分了我试图这样做。

基本上,我想

  1. 拍摄视频文件,例如存储在/tmp/myVideoFile.mp4
  2. CIFilter应用于此视频文件
  3. 将视频文件保存到其他(或相同)位置,例如/tmp/anotherVideoFile.mp4
  4. 我已经能够使用AVPlayerItemVideoOutput

    将CIFilter应用于非常轻松快速播放的视频
    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
            }
        }
    }
    

    这很好用,但我一直有很多只是在找到如何将过滤器应用于已保存的视频文件时遇到的麻烦。有一个选项,基本上只是做我上面做的,使用AVPlayer,播放视频,并在播放时从每一帧获取像素缓冲区,但这不适用于视频处理背景。我不认为用户会喜欢等待他们的视频用于过滤器的应用。

    在过度简化的代码中,我正在寻找类似的东西:

    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中工作)方式将过滤器应用于视频文件然后保存?例如,这会拍摄已保存的视频,将其加载到AVAsset,应用CIFilter,然后将新视频保存到其他位置。

1 个答案:

答案 0 :(得分:21)

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

还有一个例子in Apple's 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(/*...*/)

有关in the WWDC15 session on Core Image, starting around 20 minutes in的更多信息。

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

  

除此之外:想想你真正需要支持多久。 As of August 15, 2016,87%的设备在iOS 9.0或更高版本上,97%在设备上   iOS 8.0或更高版本。付出很多努力来支持您的潜在客户群的一小部分 - 当您完成项目并准备部署时它会变得更小 - 可能不值得花费。

有几种方法可以解决这个问题。不管怎样,您将获得CVPixelBuffer代表源框架,creating CIImages from them,应用过滤器和rendering out new CVPixelBuffers

  1. 使用AVAssetReaderAVAssetWriter来读取和写入像素缓冲区。在Apple的AVFoundation编程指南的Export章节中,有如何执行此操作的示例(读写部分;您仍需要在其间进行过滤)。

  2. AVVideoComposition与自定义合成器类一起使用。您的自定义合成器具有AVAsynchronousVideoCompositionRequest个对象,可以访问像素缓冲区,并提供一种处理像素缓冲区的方法。 Apple有一个名为AVCustomEdit的示例代码项目,它显示了如何执行此操作(再次,只是获取和返回示例缓冲区部分;您希望使用Core Image处理而不是使用其GL渲染器)。

  3. 在这两个中,AVVideoComposition路线更灵活,因为您可以使用合成进行播放和导出。