向视频文件添加过滤器的最佳方法是什么? 我是否必须从视频中提取所有帧,然后为每个帧添加滤镜?或者有更好的方法。
碰到了这个,但不知道如何使用它
let vidComp = AVVideoComposition(asset: self.asset, applyingCIFiltersWithHandler: {
request in
})
请帮忙。
答案 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中讨论。
西蒙