如何使用 avfoundation 合并视频剪辑?
how to merge video clips using avfoundation?
我已成功将视频剪辑合并为单个视频,但我在最终合并视频中遇到问题,最终视频在每个视频剪辑结束后显示一个白框。我已经尝试了很多来删除它,但找不到成功。请在下面查看我的代码。
func merge(arrayVideos:[AVAsset], completion:@escaping (_ exporter: AVAssetExportSession) -> ()) -> Void {
let mainComposition = AVMutableComposition()
let compositionVideoTrack = mainComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
compositionVideoTrack?.preferredTransform = CGAffineTransform(rotationAngle: .pi / 2)
let soundtrackTrack = mainComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
var time:Double = 0.0
for (index, videoAsset) in arrayVideos.enumerated() {
let atTime = CMTime(seconds: time, preferredTimescale: 1)
try! compositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration), of: videoAsset.tracks(withMediaType: .video)[0], at: atTime)
try! soundtrackTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration), of: videoAsset.tracks(withMediaType: .audio)[0], at: atTime)
time += videoAsset.duration.seconds
}
let outputFileURL = URL(fileURLWithPath: NSTemporaryDirectory() + "merge.mp4")
print("final URL:\(outputFileURL)")
let fileManager = FileManager()
do {
try fileManager.removeItem(at: outputFileURL)
} catch let error as NSError {
print("Error: \(error.domain)")
}
let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality)
exporter?.outputURL = outputFileURL
exporter?.outputFileType = AVFileType.mp4
exporter?.shouldOptimizeForNetworkUse = true
exporter?.exportAsynchronously {
DispatchQueue.main.async {
completion(exporter!)
}
}
}
不要使用 Double
来跟踪插入时间,这可能会因舍入错误而导致间隙。并且在转换秒时不要使用 1 的 preferredTimescale
,这将有效地将所有内容四舍五入为整秒(1000 将是更常见的时间刻度)。
而不是使用初始化为 kCMTimeZero
的 CMTime
来跟踪插入时间,并使用 CMTimeAdd
来推进它。
还有一件事:视频和音频轨道可以有不同的持续时间,特别是在录制时。因此,为了保持同步,您可能需要使用 CMTimeRangeGetIntersection
获取资产中音频和视频的公共时间范围,然后使用结果插入合成。
我已成功将视频剪辑合并为单个视频,但我在最终合并视频中遇到问题,最终视频在每个视频剪辑结束后显示一个白框。我已经尝试了很多来删除它,但找不到成功。请在下面查看我的代码。
func merge(arrayVideos:[AVAsset], completion:@escaping (_ exporter: AVAssetExportSession) -> ()) -> Void {
let mainComposition = AVMutableComposition()
let compositionVideoTrack = mainComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
compositionVideoTrack?.preferredTransform = CGAffineTransform(rotationAngle: .pi / 2)
let soundtrackTrack = mainComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
var time:Double = 0.0
for (index, videoAsset) in arrayVideos.enumerated() {
let atTime = CMTime(seconds: time, preferredTimescale: 1)
try! compositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration), of: videoAsset.tracks(withMediaType: .video)[0], at: atTime)
try! soundtrackTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration), of: videoAsset.tracks(withMediaType: .audio)[0], at: atTime)
time += videoAsset.duration.seconds
}
let outputFileURL = URL(fileURLWithPath: NSTemporaryDirectory() + "merge.mp4")
print("final URL:\(outputFileURL)")
let fileManager = FileManager()
do {
try fileManager.removeItem(at: outputFileURL)
} catch let error as NSError {
print("Error: \(error.domain)")
}
let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality)
exporter?.outputURL = outputFileURL
exporter?.outputFileType = AVFileType.mp4
exporter?.shouldOptimizeForNetworkUse = true
exporter?.exportAsynchronously {
DispatchQueue.main.async {
completion(exporter!)
}
}
}
不要使用 Double
来跟踪插入时间,这可能会因舍入错误而导致间隙。并且在转换秒时不要使用 1 的 preferredTimescale
,这将有效地将所有内容四舍五入为整秒(1000 将是更常见的时间刻度)。
而不是使用初始化为 kCMTimeZero
的 CMTime
来跟踪插入时间,并使用 CMTimeAdd
来推进它。
还有一件事:视频和音频轨道可以有不同的持续时间,特别是在录制时。因此,为了保持同步,您可能需要使用 CMTimeRangeGetIntersection
获取资产中音频和视频的公共时间范围,然后使用结果插入合成。