【问题标题】:Applying a CIFilter to a Video File and Saving it将 CIFilter 应用于视频文件并保存
【发布时间】:2016-12-31 02:10:09
【问题描述】:

是否有任何快速、尽可能轻量级的方法将CIFilter 应用于视频?在提到它之前,我已经查看了 GPUImage - 它看起来像非常强大的 magic 代码,但对于我正在尝试做的事情来说,它确实有点矫枉过正。

基本上,我想

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

我已经能够使用 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,然后将新视频保存到不同的位置。

【问题讨论】:

标签: ios swift cifilter avasset


【解决方案1】:

在 iOS 9 / OS X 10.11 / tvOS 中,有一种将CIFilters 应用于视频的便捷方法。它适用于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 或更高版本。花费大量精力来支持一小部分潜在客户群——当你完成项目并准备好部署时,它会变得更小——可能不值得。

有几种方法可以解决这个问题。无论哪种方式,您都会得到代表源帧的CVPixelBuffers、creating CIImages from them、应用过滤器和rendering out new CVPixelBuffers

  1. 使用AVAssetReaderAVAssetWriter 读取和写入像素缓冲区。在 Apple 的 AVFoundation Programming Guide 的 Export 章节中提供了如何执行此操作的示例(读取和写入部分;您仍然需要在两者之间进行过滤)。

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

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

【讨论】:

  • 感谢您的回答!我已经对AVVideoComposition 做了很多工作,但我似乎无法让获得 CVPixelBuffer 的startVideoCompositionRequest() 函数工作。当我在 AVVideoCompositionInstruction 类上设置 passthroughTrackID 时,它被调用,但没有任何轨道 ID(因此没有像素缓冲区)。但是,当我不设置轨道 ID 时,它不会被调用。
  • 您知道是什么原因造成的吗?如果它比这更复杂(可能是这样),我会发布另一个问题来询问它 - 我只是想我会先在这里问你,以防它真的很明显
  • 还有其他人因此得到模糊的结果吗?
  • 我完全复制粘贴了这段代码,但视频出来时完全是黑色的,没有声音。有什么想法吗? @rickster
  • 结构精美的摘要。
猜你喜欢
  • 2012-05-05
  • 2019-07-06
  • 2016-08-04
  • 2021-06-14
  • 2012-11-21
  • 1970-01-01
  • 2012-03-30
  • 1970-01-01
  • 2011-05-28
相关资源
最近更新 更多