向视频文件添加滤镜的最佳方法是什么? 我是否必须从视频中提取所有帧,然后为每个帧添加过滤器?或者是否有更好的方法。
遇到过这个,但不确定如何使用它
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/