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

标签 swift video filter cifilter avasset

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

遇到过这个,但不确定如何使用它

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

请帮忙。

最佳答案

这是一个相当漫长的过程。你坐得舒服吗?那我就开始……

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

vendor 类的第一项工作是从控制面板中的“加载”按钮提供的 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 中的一个错误的解决方法,视频偶尔会加载失败明显错误。其次,为了同时支持横向和纵向视频,我创建了视频轨道首选变换的倒置版本。

供应商包含一个调用 step(_:)CADisplayLink:

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,我根据 CACurrentMediaTime 计算了 AVPlayerItem 的时间。标准化时间(即介于 0 和 1 之间)是通过将播放器项目的时间除以 Assets 持续时间来计算的,UI 组件使用它来设置播放期间滑动条的位置。在 displayVideoFrame(_:) 中从 itemTime 的电影帧创建一个 CIImage:

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,我应用一个过滤器(如果有的话)并将呈现的结果返回给要显示的委托(delegate)(这是主视图):

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 中讨论了这个问题.

西蒙

关于swift - 如何将 CIFilter 添加到从图库导入的视频文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37261101/

相关文章:

swift - 如何在 Swift 中格式化时间并检查 "working hours"

ios - Swift 自定义键盘 - 在键盘长按时显示额外的字母弹出窗口?

ios - 为什么方法 updateApplicationContext 返回 "Payload contains unsupported type."

python - 如何在视频上叠加

php - 如何在Laravel中流式传输视频

ios - 是否可以通过视频/音频 IOS 发送推送通知?

ios - 未添加到 x 时创建三角形 View 失败 : 0 and y: 0

java - 有没有更好的方法来为许多分机做文件过滤?

grails - 从 Grails 中的过滤器辅助方法调用 render()

mongodb - 如何过滤子数组但保留根内容?