IOS 剪辑编辑器

最近这两年视频剪辑非常火,很多APP都内置了视频剪辑功能。

IOS视频剪辑主要依赖AVFoundation实现。

AVMutableComposition

视频剪辑需要创建一个工程,这个工程只负责把来自不同素材的视频和音频重新组合到自己的轨道track上,等于在内存里把不同素材重新构建成一个新的视频素材。因此你可以直接在AVPlayer上播放这个工程,就像播放一个正常的视频,AVMutableComposition可以等同于AVAsset被使用。

如果你只想把不同的素材拼接起来,仅使用AVMutableComposition就足够了。


composition结构

1. 插入视频轨道

let composition = AVMutableComposition()

let track = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)

2. 插入素材

var path =  Bundle.main.path(forResource:"clip", ofType:"mp4")

let asset =AVURLAsset(url:URL(fileURLWithPath: path!))

let assetTrack = asset.tracks(withMediaType: .video).first

var assetTimeRange = CMTimeRangeMake(start:CMTime.zero, duration: assetA.duration)

var startTime = CMTime.zero

try stack.insertTimeRange(assetTimeRange, of: assetTrackA, at: startTime)

插入音频的方法大同小异,如果只是为了拼接视频,到这里就足够了。

AVMutableVideoComposition

如果你想对合并后的视频素材进行处理,例如转场、特效、滤镜、文本、贴图这样的操作,你需要在AVMutableVideoComposition里完成。


video composition结构

1. 直接创建

let videoComposition = AVMutableVideoComposition(propertiesOf:composition)

当在composition上创建videoComposition之后,会自动创建AVMutableVideoCompositionInstruction和AVMutableVideoCompositionLayerInstruction。

AVMutableVideoCompositionInstruction

一个等待处理的视频剪辑composition会根据插入素材的情况被自动划分为多个可剪辑的区域,例如0-1秒或者2-5秒,你不需要去思考,当你直接创建videoComposition的时候instruction会被自动的创建出来,每一个instruction代表一个可编辑的时间范围。

AVMutableVideoCompositionLayerInstruction

如果一个视频存在2个视频轨道和2个音频轨道,instruction代表1-3秒的时间范围,那么layerInstruction会自动绑定当前这个时间范围里的所有轨道,有几个轨道就有几个layerInstruction。

利用instruction和layerInstruction转场 

假设你有两个视频,被插入到两个轨道里,第一个视频插入到track 1的0-3秒,第二个视频插入到track 2的2-5秒,那么合并后的视频长度就是5秒。中间叠加的1秒就可以进行转场的操作。


Instruction结构

let instructions = videoComposition.instructions as! [AVMutableVideoCompositionInstruction]

for instruction in instructions {           

    if instruction.layerInstructions.count < 2 {

                    continue 

    }

    var layerA = instruction.layerInstructions.first as! AVMutableVideoCompositionLayerInstruction                                        let layerB = instruction.layerInstructions.last as! AVMutableVideoCompositionLayerInstruction                         

    let fromEndTranform = CGAffineTransform(translationX: composition.naturalSize.width, y: 0)           

    let toStartTranform = CGAffineTransform(translationX: -composition.naturalSize.width, y: 0)           

    let range = CMTimeRangeMake(start: instruction.timeRange.start, duration: CMTimeMake(value: 60, timescale: 30))    let identityTransform = CGAffineTransform.identity                       

    layerB.setTransformRamp(fromStart: fromEndTranform, toEnd: identityTransform, timeRange: range)           

    layerA.setTransformRamp(fromStart: identityTransform, toEnd: toStartTranform, timeRange: range)           

    layerA.setOpacityRamp(fromStartOpacity: 1.0, toEndOpacity: 0.0, timeRange: range)                   

}

利用CGAffineTransform进行转场,两个轨道的视频都进行位移进行转场。

2. AVAsynchronousCIImageFilteringRequest

这种方式不需要instruction和layer instruction,也是简单的一种方式,只能做滤镜做不了转场,它是把整个composition看成一个。所以你能从里面截取当前的一帧视频,并对这一帧视频做处理。

let videoComposition = AVMutableVideoComposition(asset:composition) { (request)in

    let sourceImage = request.sourceImage.clampedToExtent()

    let outputImage = sourceImage.applyingFilter("CIPhotoEffectProcess")

    request.finish(with: outputImage, context:nil)

}

如果你只想给视频套个滤镜,这就足够了。

3. customVideoCompositorClass

如果你想给视频添加滤镜又想添加转场,你需要使用第三种,这种模式下可以让你针对每一帧视频做特效,并且可以针对不同的轨道做转场。

let videoComposition = AVMutableVideoComposition(propertiesOf: composition)

videoComposition.customVideoCompositorClass = CustomVideoCompositor.self

= CMTimeRangeMake(start:CMTimeMake(value:30, timescale:30), duration:  CMTimeMake(value:30, timescale:30))

let instruction =VideoCompositionInstruction(timeRange:assetTimeRange)

// instruction.timeRange = assetTimeRange

videoComposition.instructions= [instruction]

和前面的方法不同我们需要自定义两个类CustomVideoCompositor和VideoCompositionInstruction,前者用来处理每一帧的视频。如果你想传递滤镜的名称、参数,转场的参数、类别你还是需要定义后面的这个类,通过它来传递信息。

VideoCompositionInstruction

作为例子,只传递了一个时间范围

import Foundation

import AVFoundation

final class VideoCompositionInstruction: NSObject, AVVideoCompositionInstructionProtocol {

    var timeRange: CMTimeRange

    let enablePostProcessing: Bool = true

    let containsTweening: Bool = false

    var requiredSourceTrackIDs: [NSValue]?

    var passthroughTrackID: CMPersistentTrackID = kCMPersistentTrackID_Invalid

    lazy var transform:CGAffineTransform! = {

        let obj = CGAffineTransform()

        return obj

    }()

    init(timeRange: CMTimeRange) {

        self.timeRange = timeRange

        super.init()

    }

}

CustomVideoCompositor

作为例子,这个视频只有一个轨道,设定了一个转场的状态,对视频增加了一个滤镜,实际情况你还需要开发更多,但方法就是这样的,在这个类里你可以做任何想做的修改,只是比前两种方法要复杂一些。例如转场,你需要自己计算每一帧的状态,而前面你只需要指定几个参数。

import AVFoundation

import CoreImage

class CustomVideoCompositor : NSObject, AVVideoCompositing {

    private let queue = DispatchQueue(label: "com.langwan.videoclipeditor.Langwan-VideoClipEditor.render", qos: .default)

    private var renderContext: AVVideoCompositionRenderContext = AVVideoCompositionRenderContext()

    private let colorSpace = CGColorSpaceCreateDeviceRGB()

    private let ciContext: CIContext = {

        if let eaglContext = EAGLContext(api: .openGLES3) ?? EAGLContext(api: .openGLES2) {

            return CIContext(eaglContext: eaglContext)

        }

        return CIContext()

    }()

    private static let pixelFormat = kCVPixelFormatType_32BGRA

    let sourcePixelBufferAttributes: [String : Any]? = [

        kCVPixelBufferPixelFormatTypeKey as String : NSNumber(value: CustomVideoCompositor.pixelFormat),

        kCVPixelBufferOpenGLESCompatibilityKey as String : NSNumber(value: true),

    ]

    let requiredPixelBufferAttributesForRenderContext: [String : Any] = [

        kCVPixelBufferPixelFormatTypeKey as String : NSNumber(value: CustomVideoCompositor.pixelFormat),

        kCVPixelBufferOpenGLESCompatibilityKey as String : NSNumber(value: true),

    ]

    func renderContextChanged(_ newRenderContext: AVVideoCompositionRenderContext) {

        renderContext = newRenderContext

    }

    func startRequest(_ request: AVAsynchronousVideoCompositionRequest) {

        // print("startRequest")

        autoreleasepool {

            queue.async {

                guard let instruction = request.videoCompositionInstruction as? VideoCompositionInstruction else {

                    print("instruction is not VideoCompositionInstruction")

                    return

                }

                let frameBuffer = self.renderFrame(forRequest: request)

                request.finish(withComposedVideoFrame: frameBuffer)

            }

        }

    }

    private func renderFrame(forRequest request: AVAsynchronousVideoCompositionRequest) -> CVPixelBuffer {

        let stackId = request.sourceTrackIDs[0]

        guard let frameBuffer = request.sourceFrame(byTrackID: CMPersistentTrackID(stackId)) else {

            let blankBuffer = self.renderContext.newPixelBuffer()

            return blankBuffer!

        }

        let sourceImage = CIImage(cvPixelBuffer: frameBuffer)

        //let filter = CIFilter(name: "CIPhotoEffectProcess")!

        let w = CVPixelBufferGetWidth(frameBuffer)

        let transform = CGAffineTransform(translationX: CGFloat(w / 2), y: 0)

        let outputImage = sourceImage.applyingFilter("CIPhotoEffectProcess").transformed(by: transform)

        let renderedBuffer = renderContext.newPixelBuffer()

        ciContext.render(outputImage, to: renderedBuffer!, bounds: outputImage.extent, colorSpace: self.colorSpace)

        return renderedBuffer!

    }

}

具体的例子可以参考 swift_video_clip_editor_exmaple 并下载源代码。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Editing - 编辑 The AVFoundation framework provides a featur...
    啊俊吖阅读 891评论 0 1
  • 文章目录 1. Editing - 编辑1.1. Creating a Composition - 创建组件1.1...
    __season____阅读 783评论 0 2
  • 本篇文章是基于谷歌有关Graphic的一篇概览文章的翻译:http://source.android.com/de...
    lee_3do阅读 7,227评论 2 21
  • 原文:AVFoundation Programming Guide 写在前面 简单翻译一下AVFoundation...
    朦胧1919阅读 1,619评论 0 1
  • AVFoundation框架提供了一组功能丰富的类,以简化音视频的编辑。compositions是AVFounda...
    smm987阅读 650评论 0 0