跳到主要内容

视频编辑与合成

问题

如何用 AVFoundation 进行视频剪辑和合成?

答案

AVAsset 与 AVComposition

import AVFoundation

func mergeVideos(urls: [URL]) async throws -> URL {
let composition = AVMutableComposition()
let videoTrack = composition.addMutableTrack(
withMediaType: .video,
preferredTrackID: kCMPersistentTrackID_Invalid
)!
let audioTrack = composition.addMutableTrack(
withMediaType: .audio,
preferredTrackID: kCMPersistentTrackID_Invalid
)!

var currentTime = CMTime.zero
for url in urls {
let asset = AVURLAsset(url: url)
let duration = try await asset.load(.duration)
let timeRange = CMTimeRange(start: .zero, duration: duration)

if let video = try await asset.loadTracks(withMediaType: .video).first {
try videoTrack.insertTimeRange(timeRange, of: video, at: currentTime)
}
if let audio = try await asset.loadTracks(withMediaType: .audio).first {
try audioTrack.insertTimeRange(timeRange, of: audio, at: currentTime)
}
currentTime = CMTimeAdd(currentTime, duration)
}

// 导出
let outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("merged.mp4")
let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)!
exporter.outputURL = outputURL
exporter.outputFileType = .mp4
await exporter.export()
return outputURL
}

添加水印

func addWatermark(to asset: AVAsset, text: String) -> AVVideoComposition {
let videoSize = CGSize(width: 1920, height: 1080)

let watermarkLayer = CATextLayer()
watermarkLayer.string = text
watermarkLayer.fontSize = 40
watermarkLayer.foregroundColor = UIColor.white.withAlphaComponent(0.7).cgColor
watermarkLayer.frame = CGRect(x: videoSize.width - 300, y: 20, width: 280, height: 50)

let parentLayer = CALayer()
parentLayer.frame = CGRect(origin: .zero, size: videoSize)
let videoLayer = CALayer()
videoLayer.frame = parentLayer.frame
parentLayer.addSublayer(videoLayer)
parentLayer.addSublayer(watermarkLayer)

let composition = AVMutableVideoComposition()
composition.frameDuration = CMTime(value: 1, timescale: 30)
composition.renderSize = videoSize
composition.animationTool = AVVideoCompositionCoreAnimationTool(
postProcessingAsVideoLayer: videoLayer, in: parentLayer
)
return composition
}

常见面试问题

Q1: AVAssetExportSession 导出很慢怎么优化?

答案

  • 选择合适的 preset(不必总是 HighestQuality
  • 只处理需要的时间范围
  • 大量操作时使用 AVAssetWriter 替代(更底层、更灵活)

相关链接