I am using AvMutableComposition swift to overlay a video inside another one. The below code renders the correct shape for the overlay, a circle, but uses the same video source for both the main and the overlay layer.
func overlayVideo(mainVideoURL:URL,overlayVideoURL:URL,completion:@escaping Completion) -> Void{
var arrayLayerInstructions:[AVMutableVideoCompositionLayerInstruction] = []
let mainVideoURL = mainVideoURL
let overlayVideoURL = overlayVideoURL
let mainVideoAsset = AVAsset(url: mainVideoURL)
let overlayVideoAsset = AVAsset(url: overlayVideoURL)
guard let mainVideoTrack = mainVideoAsset.tracks(withMediaType: .video).first else {
return
}
guard let overlayVideoTrack = overlayVideoAsset.tracks(withMediaType: .video).first else {
return
}
let composition = AVMutableComposition.init()
let mainCompositionVideoTrack = composition.addMutableTrack(withMediaType:.video,preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
do {
try mainCompositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: mainVideoAsset.duration),of: mainVideoTrack,at: .zero)
} catch {
print("Error inserting video tracks: \(error)")
return
}
let overlayCompositionVideoTrack = composition.addMutableTrack(withMediaType:.video,preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
do {
try overlayCompositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: overlayVideoAsset.duration),of: overlayVideoTrack,at: .zero)
} catch {
print("Error inserting video tracks: \(error)")
return
}
let outputSize = CGSize(width: mainVideoTrack.naturalSize.width, height: mainVideoTrack.naturalSize.height)
let videoLayer = CALayer()
videoLayer.frame = CGRect(x: 0, y: 0, width: outputSize.width, height: outputSize.height)
let overlayLayer = CALayer()
overlayLayer.frame = CGRect(x: 150, y: 200, width: 500, height: 500)
let maskPath = UIBezierPath(ovalIn: overlayLayer.bounds)
let maskShapeLayer = CAShapeLayer()
maskShapeLayer.path = maskPath.cgPath
overlayLayer.mask = maskShapeLayer
overlayLayer.borderWidth = 2.0
overlayLayer.borderColor = UIColor.white.cgColor
overlayLayer.masksToBounds = true
videoLayer.addSublayer(overlayLayer)
// Add instruction for video track
let mainLayerInstruction = videoCompositionInstructionForTrack(track: mainCompositionVideoTrack!,asset: mainVideoAsset,standardSize: outputSize,atTime: .zero)
mainLayerInstruction.trackID = mainCompositionVideoTrack!.trackID
arrayLayerInstructions.append(mainLayerInstruction)
//2
let overlayLayerInstruction = videoCompositionInstructionForTrack(track: overlayCompositionVideoTrack!,asset: overlayVideoAsset,standardSize: CGSize(width: 500, height: 500),atTime:overlayVideoAsset.duration)
overlayLayerInstruction.trackID = overlayCompositionVideoTrack!.trackID
arrayLayerInstructions.append(overlayLayerInstruction)
// Main video composition instruction
let mainInstruction = AVMutableVideoCompositionInstruction()
mainInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: composition.duration)
mainInstruction.layerInstructions = arrayLayerInstructions
// Main video composition
let mainComposition = AVMutableVideoComposition()
mainComposition.instructions = [mainInstruction]
mainComposition.frameDuration = CMTimeMake(value: 1,timescale: 30)
mainComposition.renderSize = outputSize
mainComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayers: [overlayLayer,videoLayer], in: videoLayer)
//save export
let path = NSTemporaryDirectory().appending("strory.mp4")
let exportURL = URL.init(fileURLWithPath: path)
try? FileManager.default.removeItem(atPath: path)
let exporter = AVAssetExportSession.init(asset: composition, presetName:AVAssetExportPresetHighestQuality)
exporter?.outputURL = exportURL
exporter?.outputFileType = .mp4
exporter?.shouldOptimizeForNetworkUse = true
exporter?.videoComposition = mainComposition
exporter?.exportAsynchronously(completionHandler: {
switch exporter?.status {
case .completed:
print("Export completed!")
completion(exportURL,nil)
case .failed, .unknown, .cancelled:
print("Export failed: \(exporter?.error?.localizedDescription ?? "")")
completion(nil,exporter?.error)
default:
break
}
})
}
The result looks as follows:
How can I use two different video streams for the main layer and the overlay?