CIFilter
をビデオに適用するための高速で軽量な方法はありますか?言及する前に、私はhaveを見て GPUImage -それは非常に強力なように見えます 魔法 コードですが、私がやろうとしていることには本当にやり過ぎです。
本質的に、私はしたい
/tmp/myVideoFile.mp4
に保存されていると言うビデオファイルを取りますCIFilter
を適用します/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 9/OS X 10.11/tvOSには、ビデオにCIFilter
sを適用するための便利な方法があります。 AVVideoComposition
で動作するため、再生とファイル間のインポート/エクスポートの両方に使用できます。メソッドのドキュメントについては、 AVVideoComposition.init(asset:applyingCIFiltersWithHandler:)
を参照してください。
例があります AppleのCore Imageプログラミングガイド もあります:
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(/*...*/)
これについてはもう少しあります Core ImageのWWDC15セッションで、約20分で始まります 。
以前のOSで動作するソリューションが必要な場合は、もう少し複雑です。
余談ですが、本当にサポートする必要があるのはどれくらい前かを考えてください。 2016年8月15日現在 、デバイスの87%がiOS 9.0以降を使用し、97%がiOS8.0以降を使用しています。潜在的な顧客ベースのごく一部をサポートするために多大な努力を払うと、プロジェクトが完了して展開の準備が整うまでにさらに小さくなりますが、コストに見合う価値はないかもしれません。
これを行うには、いくつかの方法があります。いずれにせよ、ソースフレームを表すCVPixelBuffer
sを取得し、 それらからCIImage
sを作成 、フィルターを適用し、 新しいCVPixelBuffer
s 。
ピクセルバッファの読み取りと書き込みには、AVAssetReader
とAVAssetWriter
を使用します。これを行う方法の例(読み取りと書き込みの部分。その間にフィルタリングを行う必要があります)は、AppleのAVFoundationプログラミングガイドの Export の章にあります。
カスタムコンポジタークラスでAVVideoComposition
を使用します。カスタムコンポジターには、ピクセルバッファーへのアクセスを提供するオブジェクトと、処理されたピクセルバッファーを提供する方法が提供されます AVAsynchronousVideoCompositionRequest
Appleには AVCustomEdit というサンプルコードプロジェクトがあり、これを行う方法を示しています(ここでも、サンプルバッファの取得と戻りの部分だけです。Coreで処理する必要があります) GLレンダラー)を使用する代わりに画像。
これら2つのうち、AVVideoComposition
ルートは、再生とエクスポートの両方にコンポジションを使用できるため、より柔軟です。