Swiftで動画編集
swiftで動画(AVAsset)を逆再生や、早送りにして書き出すのをやってみた。
方法としては、
AVAsset
を[CGImage]
に変換する- それを並び替えたりして、
CAKeyframeAnimation
を仕込んだCALayer
に変換する addSublayer
でプレビューする- MOVとして書き出す
まずは、動画を画像配列に変換
extension AVAsset { func frames() -> [CGImage] { var images = [CGImage]() guard let track = tracksWithMediaType(AVMediaTypeVideo).first else { return [CGImage]() } var reader: AVAssetReader? do { reader = try AVAssetReader(asset: self) } catch let error as NSError { print(error) } let options = [ "\(kCVPixelBufferPixelFormatTypeKey)": Int(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) ] let output = AVAssetReaderTrackOutput(track: track, outputSettings: options) reader?.addOutput(output) reader?.startReading() while let sample = output.copyNextSampleBuffer() { guard let pixelBuffer = CMSampleBufferGetImageBuffer(sample) else { return [CGImage]() } let ciImage = CIImage(CVPixelBuffer: pixelBuffer) let context = CIContext(options: [kCIContextUseSoftwareRenderer: true]) let cgImage = context.createCGImage(ciImage, fromRect: ciImage.extent) images.append(cgImage) } return images } }
次にその画像配列を並び替えたりして、アニメーションレイヤーを作成
任意の画像とdurationのセットで渡します
struct KeyFrame { internal let image: CGImage internal let duration: Double }
extension CALayer { class func animation(keyFrames: [KeyFrame]) -> CALayer { let total = keyFrames.map({ $0.duration }).reduce(0, combine: +) let count = keyFrames.count var times = [Double]() var currentTime = 0.0 for i in 0..<count { times.append(currentTime/total) currentTime += keyFrames[i].duration } let layer = CALayer() let animation = CAKeyframeAnimation(keyPath: "contents") animation.keyTimes = times animation.values = keyFrames.map { $0.image } animation.duration = total animation.repeatCount = .infinity animation.beginTime = AVCoreAnimationBeginTimeAtZero animation.removedOnCompletion = false animation.calculationMode = kCAAnimationDiscrete layer.addAnimation(animation, forKey: "contents") return layer } }
これであとは、
let animationLayer = layer.animation(keyFrames) layer.frame = view.frame previewView.layer.addSublayer(layer)
みたいな感じでプレビユーできた!
最後書き出しはAVAssetExportSession
で簡単!