Adding overlay to video in Swift 3

Viewed 3099

I'm learning AVFoundation and I'm having a problem trying to save a video with an overlay image in Swift 3. Using AVMutableComposition I'm able to add the image to the video however the video is zoomed in and not constraining itself to the portrait size the video was taken in. I've tried:

  • Setting the natural size through the AVAssetTrack.
  • Constraining the video to portrait size in the AVMutableVideoComposition renderFrame.
  • Locking the new video bounds to the recorded video width and height.

The code below works apart from the issue I'm needing help on. The image I'm trying to add covers the entire portrait view and has a border all around the edges. The app also only allows for portrait.

func processVideoWithWatermark(video: AVURLAsset, watermark: UIImage, completion: @escaping (Bool) -> Void) {

    let composition = AVMutableComposition()
    let asset = AVURLAsset(url: video.url, options: nil)

    let track =  asset.tracks(withMediaType: AVMediaTypeVideo)
    let videoTrack:AVAssetTrack = track[0] as AVAssetTrack
    let timerange = CMTimeRangeMake(kCMTimeZero, asset.duration)

    let compositionVideoTrack:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())

    do {
        try compositionVideoTrack.insertTimeRange(timerange, of: videoTrack, at: kCMTimeZero)
        compositionVideoTrack.preferredTransform = videoTrack.preferredTransform
    } catch {
        print(error)
    }

//      let compositionAudioTrack:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
//      
//      for audioTrack in asset.tracks(withMediaType: AVMediaTypeAudio) {
//          do {
//              try compositionAudioTrack.insertTimeRange(audioTrack.timeRange, of: audioTrack, at: kCMTimeZero)
//          } catch {
//              print(error)
//          }
//          
//      }
//      
    let size = videoTrack.naturalSize

    let watermark = watermark.cgImage
    let watermarklayer = CALayer()
    watermarklayer.contents = watermark
    watermarklayer.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
    watermarklayer.opacity = 1

    let videolayer = CALayer()
    videolayer.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)

    let parentlayer = CALayer()
    parentlayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    parentlayer.addSublayer(videolayer)
    parentlayer.addSublayer(watermarklayer)

    let layercomposition = AVMutableVideoComposition()
    layercomposition.frameDuration = CMTimeMake(1, 30)
    layercomposition.renderSize = CGSize(width: screenWidth, height: screenHeight)
    layercomposition.renderScale = 1.0
    layercomposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videolayer, in: parentlayer)

    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, composition.duration)

    let videotrack = composition.tracks(withMediaType: AVMediaTypeVideo)[0] as AVAssetTrack
    let layerinstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videotrack)

    layerinstruction.setTransform(videoTrack.preferredTransform, at: kCMTimeZero)

    instruction.layerInstructions = [layerinstruction]
    layercomposition.instructions = [instruction]

    let filePath = NSTemporaryDirectory() + self.fileName()
    let movieUrl = URL(fileURLWithPath: filePath)

    guard let assetExport = AVAssetExportSession(asset: composition, presetName:AVAssetExportPresetHighestQuality) else {return}
    assetExport.videoComposition = layercomposition
    assetExport.outputFileType = AVFileTypeMPEG4
    assetExport.outputURL = movieUrl

    assetExport.exportAsynchronously(completionHandler: {

        switch assetExport.status {
        case .completed:
            print("success")
            print(video.url)
            self.saveVideoToUserLibrary(fileURL: movieUrl, completion: { (success, error) in
                if success {
                    completion(true)
                } else {
                    completion(false)

                }
            })

            break
        case .cancelled:
            print("cancelled")
            break
        case .exporting:
            print("exporting")
            break
        case .failed:
            print(video.url)
            print("failed: \(assetExport.error!)")
            break
        case .unknown:
            print("unknown")
            break
        case .waiting:
            print("waiting")
            break
        }
    })

}
1 Answers
Related