0

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:

Output video screenshot of

How can I use two different video streams for the main layer and the overlay?

Adriaan
  • 17,741
  • 7
  • 42
  • 75

0 Answers0