如何将CIFilter添加到从库导入的视频文件中

时间:2016-05-16 18:48:36

标签: swift video filter cifilter avasset

向视频文件添加过滤器的最佳方法是什么? 我是否必须从视频中提取所有帧,然后为每个帧添加滤镜?或者有更好的方法。

碰到了这个,但不知道如何使用它

    let vidComp = AVVideoComposition(asset: self.asset, applyingCIFiltersWithHandler: {
     request in
    })

请帮忙。

1 个答案:

答案 0 :(得分:1)

这是一个漫长的过程。你坐得舒服吗?然后我会开始......

作为我的VideoEffects项目的一部分,我创建了一个名为FilteredVideoVendor的类,它会从电影文件中过滤掉过滤后的图像帧。

供应商类的第一项工作是从控制面板中“加载”按钮提供的URL实际打开一部电影:

func openMovie(url: NSURL){
player = AVPlayer(URL: url)

guard let player = player,
  currentItem = player.currentItem,
  videoTrack = currentItem.asset.tracksWithMediaType(AVMediaTypeVideo).first else {
    fatalError("** unable to access item **")
}

currentURL = url
failedPixelBufferForItemTimeCount = 0

currentItem.addOutput(videoOutput)

videoTransform = CGAffineTransformInvert(videoTrack.preferredTransform)

player.muted = true

}

这里有一些有趣的观点:首先,我重置了一个名为failedPixelBufferForItemTimeCount的变量 - 这是我认为AVFoundation中的一个错误的解决方法,其中视频偶尔无法加载而没有明显的错误。其次,为了支持横向和纵向视频,我创建了视频轨道首选变换的反转版本。

供应商包含CADisplayLink,可调用step(_:)

func step(link: CADisplayLink) {
guard let player = player,
  currentItem = player.currentItem else {
    return
}

let itemTime = videoOutput.itemTimeForHostTime(CACurrentMediaTime())

displayVideoFrame(itemTime)

let normalisedTime = Float(itemTime.seconds / currentItem.asset.duration.seconds)

delegate?.vendorNormalisedTimeUpdated(normalisedTime)

if normalisedTime >= 1.0
{
  paused = true
}
}

使用CADisplayLink,我根据AVPlayerItem计算CACurrentMediaTime的时间。通过将玩家项目的时间除以资产持续时间来计算归一化时间(即,在0和1之间),UI组件使用该时间来在回放期间设置擦洗条的位置。在CIImage的{​​{1}}电影的框架中创建itemTime已在displayVideoFrame(_:)中完成:

func displayVideoFrame(time: CMTime) {
guard let player = player,
  currentItem = player.currentItem where player.status == .ReadyToPlay && currentItem.status == .ReadyToPlay else {
    return
}

if videoOutput.hasNewPixelBufferForItemTime(time) {
  failedPixelBufferForItemTimeCount = 0

  var presentationItemTime = kCMTimeZero

  guard let pixelBuffer = videoOutput.copyPixelBufferForItemTime(
    time,
    itemTimeForDisplay: &presentationItemTime) else {
      return
  }

  unfilteredImage = CIImage(CVImageBuffer: pixelBuffer)

  displayFilteredImage()
}
else if let currentURL = currentURL where !paused {
  failedPixelBufferForItemTimeCount += 1

  if failedPixelBufferForItemTimeCount > 12 {
    openMovie(currentURL)
  }
}
}

在从视频输出中复制像素缓冲区之前,我需要确保有一个可用。如果这一切都很好,那么从该像素缓冲区创建CIImage是一个简单的步骤。但是,如果hasNewPixelBufferForItemTime(_:)失败次数太多(12似乎有效),我认为AVFoundation已经无声地失败了,我重新打开了电影。

使用填充的CIImage,我应用一个过滤器(如果有的话)并将渲染的结果返回给要显示的委托(这是主视图):

func displayFilteredImage() {
guard let unfilteredImage = unfilteredImage,
  videoTransform = videoTransform else {
    return
}

let ciImage: CIImage

if let ciFilter = ciFilter {
  ciFilter.setValue(unfilteredImage, forKey: kCIInputImageKey)

  ciImage = ciFilter.outputImage!.imageByApplyingTransform(videoTransform)
}
else {
  ciImage = unfilteredImage.imageByApplyingTransform(videoTransform)
}

let cgImage = ciContext.createCGImage(
  ciImage,
  fromRect: ciImage.extent)

delegate?.finalOutputUpdated(UIImage(CGImage: cgImage))
}

如果您想将过滤后的电影写回文件系统,我会在this blog post中讨论。

西蒙