StackGU
StackGU

Reputation: 958

Adding image on video with AVMutableComposition - Swift - Programmatically

I want to add a Image over a video and in order to do so I follow this tutorial:

The code I implemented is this:

func addViewToVideo(fromVideoAt videoURL: URL, withView: UIView, completion: @escaping (URL?) -> Void) {

        let imageTransformedFromView = withView.asImage()
        
        let asset = AVURLAsset(url: videoURL)
        let composition = AVMutableComposition()
        
        
        guard let compositionTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), let assetTrack = asset.tracks(withMediaType: .video).first else{
            print("Something is wrong with the asset")
            completion(nil)
            return
        }
        
        do{
            let timeRange = CMTimeRange(start: .zero, duration: asset.duration)
            
            try compositionTrack.insertTimeRange(timeRange, of: assetTrack, at: .zero)
            
            if let audioAssetTrack = asset.tracks(withMediaType: .audio).first, let compositionAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid){
                try compositionAudioTrack.insertTimeRange(timeRange, of: audioAssetTrack, at: .zero)
            }
        }catch{
            print(error)
            completion(nil)
            return
        }
        
        
        compositionTrack.preferredTransform = assetTrack.preferredTransform
        
        let videoSize: CGSize
        videoSize = CGSize(
            width: assetTrack.naturalSize.height,
            height: assetTrack.naturalSize.width)
        
        
        let videoLayer = CALayer()
        videoLayer.frame = CGRect(origin: .zero, size: videoSize)
        let overlayLayer = CALayer()
        overlayLayer.frame = CGRect(origin: .zero, size: videoSize)
        
        addImage(imagePassed: imageTransformedFromView, to: overlayLayer, videoSize: videoSize)
        
        
        let outputLayer = CALayer()
        outputLayer.frame = CGRect(origin: .zero, size: videoSize)
        outputLayer.addSublayer(videoLayer)
        outputLayer.addSublayer(overlayLayer)
        
        
        
        
        let videoComposition = AVMutableVideoComposition()
        videoComposition.renderSize = videoSize
        videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
        videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(
          postProcessingAsVideoLayer: videoLayer,
          in: outputLayer)

        
        
        let instruction = AVMutableVideoCompositionInstruction()
        instruction.timeRange = CMTimeRange(start: .zero,duration: composition.duration)
        videoComposition.instructions = [instruction]
        let layerInstruction = compositionLayerInstruction(
          for: compositionTrack,
            assetTrack: assetTrack)
        instruction.layerInstructions = [layerInstruction]
        



guard let export = AVAssetExportSession(
  asset: composition,
  presetName: AVAssetExportPreset1920x1080)
  else {
    print("Cannot create export session.")
    completion(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:
              completion(exportURL)
            default:
              print("Something went wrong during export.")
              print(export.error ?? "unknown error")
              completion(nil)
              break
        }
    }
}
}


func compositionLayerInstruction(for track: AVCompositionTrack, assetTrack: AVAssetTrack) -> AVMutableVideoCompositionLayerInstruction {

    let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
    
    var transform = CGAffineTransform.identity
    let assetSize = assetTrack.naturalSize
        
    transform = CGAffineTransform(a: 0, b: 1, c: -1, d: 0, tx: assetSize.height, ty: 0)
    
    instruction.setTransform(transform, at: .zero)
    
    return instruction
}

It works pretty fine apart from the fact that when I pass a video from the front camera it doesn't mirror it.

I'm pretty sure the reason why it doesn't mirror it stays behind the CGAffineTransform transformation.

To be honest I don't know how to work with the a,b,c,d,tx,ty constructors, but how could I transform the video such that it seems it's mirrored?

Upvotes: 1

Views: 902

Answers (0)

Related Questions