3
votes

This function is exporting the merged composition to a landscape orientation when the source video is in portrait. I save the original video in portrait orientation to my documents directory and then save it to camera roll and works fine. I then pass the saved video's url to this function and it somehow rotates it to landscape when it shouldn't. How do I fix this?

func makeVideoOverlay (url : URL) {

    print("documents directory url: \(url)")

    let composition = AVMutableComposition()
    let vidAsset = AVURLAsset(url: url as URL, options: nil)

    // get video track

    let vtrack =  vidAsset.tracks(withMediaType: AVMediaTypeVideo)
    let videoTrack:AVAssetTrack = vtrack[0]
    let vid_duration = videoTrack.timeRange.duration
    let vid_timerange = CMTimeRangeMake(kCMTimeZero, vidAsset.duration)

    //var error: NSError?

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

    do {
        try compositionvideoTrack.insertTimeRange(vid_timerange, of: videoTrack, at: kCMTimeZero)
    } catch {
        // handle error

        print("comp video track error: \(error.localizedDescription)")
    }

    compositionvideoTrack.preferredTransform = videoTrack.preferredTransform

    let size = videoTrack.naturalSize

    //this prints out to 1920x1080 landscape dimension. i don't know how

    print("asset size: \(size)")

    // Watermark Effect

    let imglogo = UIImage(named: "logo-image")
    let imglayer = CALayer()
    imglayer.contents = imglogo?.cgImage
    imglayer.frame = CGRect.init(x: 5, y: size.height-160, width: 150, height: 150)
    imglayer.opacity = 1.0

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

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

    parentlayer.addSublayer(videolayer)
    parentlayer.addSublayer(imglayer)

    let layercomposition = AVMutableVideoComposition()
    layercomposition.frameDuration = CMTimeMake(1, 30)
    layercomposition.renderSize = size
    layercomposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videolayer, in: parentlayer)

    // instruction for watermark

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

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

    instruction.layerInstructions = NSArray(object: layerinstruction) as [AnyObject] as [AnyObject] as! [AVVideoCompositionLayerInstruction]

    layercomposition.instructions = NSArray(object: instruction) as [AnyObject] as [AnyObject] as! [AVVideoCompositionInstructionProtocol]

    //  create new file to receive data

    let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let docsDir: String = dirPaths[0] as String


    let movieFilePath = docsDir.appending("/result.mov") as String

    movieDestinationUrl = URL(fileURLWithPath: movieFilePath)

    print("overlay destination url: \(movieDestinationUrl)")

    // use AVAssetExportSession to export video

    let assetExport = AVAssetExportSession(asset: composition, presetName:AVAssetExportPresetHighestQuality)

    assetExport?.outputFileType = AVFileTypeQuickTimeMovie
    assetExport?.outputURL = movieDestinationUrl as URL

    assetExport?.videoComposition = layercomposition

    assetExport?.exportAsynchronously(completionHandler: {

        if assetExport?.status == AVAssetExportSessionStatus.failed
        {
            print("failed: \(assetExport?.error)")
        }
        else if assetExport?.status == AVAssetExportSessionStatus.cancelled
        {
            print("cancelled: \(assetExport?.error)")
        }
        else
        {
            print("Movie complete")

            OperationQueue.main.addOperation({ () -> Void in

                //saves in landscape
                self.saveAsset(url: self.movieDestinationUrl)                    
            })
        }
    })
}
1

1 Answers

-1
votes

AVMutableVideoCompositionLayerInstruction has a method setTransform(_:at:)

As documentation say

Sets a fixed transform to apply from the specified time until the next time at which a transform is set. [...]. Before the first specified time for which a transform is set, the affine transform is held constant at the value of identity ; after the last time for which a transform is set, the affine transform is held constant at that last value.

You should set videoTrack's preferredTransform to layerInstruction instead.

EDIT

You need to create layerinstruction with the new created composition track instead.

let layerinstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: compositionvideoTrack) // NOT videoTrack. layerinstruction.setTransform(videoTrack.preferredTransform, at: kCMTimeZero)