これはかなり長いプロセスです。あなたは快適に座っていますか?その後、私は始めるでしょう...
私の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のバグで、明らかにエラーがなくても読み込めない場合があると私は思いますが、これを回避する方法です。次に、風景と肖像画の両方のビデオをサポートするために、ビデオトラックの優先変換の逆のバージョンを作成します。私はCACurrentMediaTime
に基づいてAVPlayerItem
のための時間を計算し、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
}
}
:
ベンダーはstep(_:)
呼び出すCADisplayLink
含まれています。正規化された時間(すなわち、0と1との間の)は、プレイヤアイテムの時間をアセット持続時間で割ることによって計算され、プレイ中のスクラブバーの位置を設定するためにUIコンポーネントによって使用される。 itemTime
でムービーのフレームからCIImage
を作成するには、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)
}
}
}
ビデオ出力からピクセルバッファをコピーする前に、私は1つが利用可能であることを確認する必要があります。それでも問題がなければ、そのピクセルバッファーからCIImage
を作成するのは簡単なステップです。しかし、hasNewPixelBufferForItemTime(_:)
が何度も失敗すると(12がうまくいくと思われます)、私はAVFoundationが黙って失敗したと判断して、ムービーを再度開きます。人口CIImage
で
(1がある場合)、私はフィルタを適用し、表示されるように背面(メインビューである)デリゲートにレンダリングされた結果を返す:
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で議論します。
シモン