我正在为视频创建两个图层,并尝试在每个单独的图层上显示我自己的 URL 和视频。我做错了什么?我在视频层和覆盖层上仅显示最终结果中的第一个视频。
问题:在最终输出视频中,我在视频层和覆盖层上仅看到来自第一个 URL (videoURL) 的视频。覆盖层未按预期显示来自overlayVideoURL 的视频。
我尝试过的:
我检查了 CALayer 框架并确认它们设置正确。
已验证 AVVideoCompositionCoreAnimationTool 是否正确使用。
确认 AVMutableComposition 已插入两条轨道。
问题:
我设置视频图层的方式是否有错误?
如何保证每一层正确显示各自的视频?
其他详细信息:
视频已正确加载并插入到 AVMutableComposition 中。
最终的合成仅在两层上显示来自 videoURL 的视频。
func makeVideo(videoURL: URL, overlayVideoURL: URL, forName name: String, onComplete: @escaping (URL?) -> Void) {
let asset = AVURLAsset(url: videoURL)
let overlayAsset = AVURLAsset(url: overlayVideoURL)
let composition = AVMutableComposition()
guard
let compositionTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid),
let assetTrack = asset.tracks(withMediaType: .video).first,
let overlayCompositionTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid),
let overlayAssetTrack = overlayAsset.tracks(withMediaType: .video).first
else {
print("Something is wrong with the assets.")
onComplete(nil)
return
}
do {
let timeRange = CMTimeRange(start: .zero, duration: asset.duration)
try compositionTrack.insertTimeRange(timeRange, of: assetTrack, at: .zero)
try overlayCompositionTrack.insertTimeRange(timeRange, of: overlayAssetTrack, at: .zero)
} catch {
print(error)
onComplete(nil)
return
}
let videoInfo = orientation(from: assetTrack.preferredTransform)
let videoSize = videoInfo.isPortrait ? CGSize(width: assetTrack.naturalSize.height, height: assetTrack.naturalSize.width) : assetTrack.naturalSize
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = videoSize
videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRange(start: .zero, duration: composition.duration)
let layerInstruction = compositionLayerInstruction(for: compositionTrack, assetTrack: assetTrack)
let overlayLayerInstruction = compositionLayerInstruction(for: overlayCompositionTrack, assetTrack: overlayAssetTrack)
instruction.layerInstructions = [layerInstruction, overlayLayerInstruction]
videoComposition.instructions = [instruction]
let videoLayer = CALayer()
videoLayer.frame = CGRect(origin: .zero, size: videoSize)
let overlayVideoLayer = CALayer()
overlayVideoLayer.frame = CGRect(x: videoSize.width / 4, y: videoSize.height / 4, width: videoSize.width / 2, height: videoSize.height / 2)
let outputLayer = CALayer()
outputLayer.frame = CGRect(origin: .zero, size: videoSize)
outputLayer.addSublayer(videoLayer)
outputLayer.addSublayer(overlayVideoLayer)
videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayers: [videoLayer, overlayVideoLayer], in: outputLayer)
guard let export = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else {
print("Cannot create export session.")
onComplete(nil)
return
}
let videoName = UUID().uuidString
let exportURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(videoName).appendingPathExtension("mov")
export.videoComposition = videoComposition
export.outputFileType = .mov
export.outputURL = exportURL
export.exportAsynchronously {
DispatchQueue.main.async {
switch export.status {
case .completed:
onComplete(exportURL)
default:
print("Something went wrong during export.")
print(export.error ?? "unknown error")
onComplete(nil)
}
}
}
}
尝试使用
AVMutableVideoCompositionLayerInstruction
而不是 AVMutableVideoComposition
。计划是这样的:
// Here how to build it:
let mainInstruction = AVMutableVideoCompositionInstruction()
// Set time of your result video duration
mainInstruction.timeRange = timeRange
// For each video you'll need a separate AVMutableVideoCompositionInstruction
let videoInstruction = AVMutableVideoCompositionInstruction()
let overlayInstruction = AVMutableVideoCompositionInstruction()
// Now how to build and setup those instructions
// Here's your asset
let asset = AVAsset(url: url)
if let audioTrack = asset.tracks(withMediaType: .audio).first,
let compositionAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
// Add audio track
}
let track = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)!
try! track.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: asset.tracks(withMediaType: .video).first!, at: .zero)
let instruction /*videoInstruction/overlayInstruction*/ = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
/* frameTransform shoule be calculated to move video track to the proper location on the layer */
instruction.setTransform(transform.concatenating(frameTransform), at: .zero)
// It will combine instructions for several layers
mainInstruction.layerInstructions = [videoInstruction, overlayInstruction]
// Video composition should have only one instruction
videoComposition.instructions = [mainInstruction]
// Then apply add videoComposition to a single layer, this single layer will render both videos in the locations determined by trannsform
videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)
抱歉代码混乱,它所基于的示例更加混乱,所以我尝试从中提取重要的内容