Search code examples
iosswiftavfoundationavcapturesessionavcapturedevice

Record crop video square aspect ratio AVCaptureSession


How can I record a square video using AVCaptureSession? If it's not possible when recording, then how can I crop it afterwards, in didFinishRecordingTo delegate method?

Thanks in advance!


Solution

  • You can record square video using this demo code: https://github.com/DarshanRlogical/DKCustomCamera

    OR

    You can crop video by using below method:

    func manageCroppingToSquare(filePath: URL , completion: @escaping (_ outputURL : URL?) -> ()) {
        
        // output file
        let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
        let outputPath = documentsURL?.appendingPathComponent("squareVideo.mov")
        if FileManager.default.fileExists(atPath: (outputPath?.path)!) {
            do {
               try FileManager.default.removeItem(atPath: (outputPath?.path)!)
            }
            catch {
                print ("Error deleting file")
            }
        }
        
        //input file
        let asset = AVAsset.init(url: filePath)
        print (asset)
        let composition = AVMutableComposition.init()
        composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
        
        //input clip
        let clipVideoTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0]
        
        //make it square
        let videoComposition = AVMutableVideoComposition()
        videoComposition.renderSize = CGSize(width: CGFloat(clipVideoTrack.naturalSize.height), height: CGFloat(clipVideoTrack.naturalSize.height))
        videoComposition.frameDuration = CMTimeMake(1, 30)
        let instruction = AVMutableVideoCompositionInstruction()
        instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30))
        
        //rotate to potrait
        let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: clipVideoTrack)
        let t1 = CGAffineTransform(translationX: clipVideoTrack.naturalSize.height, y: -(clipVideoTrack.naturalSize.width - clipVideoTrack.naturalSize.height) / 2)
        let t2: CGAffineTransform = t1.rotated(by: .pi/2)
        let finalTransform: CGAffineTransform = t2
        transformer.setTransform(finalTransform, at: kCMTimeZero)
        instruction.layerInstructions = [transformer]
        videoComposition.instructions = [instruction]
        
        //exporter 
        let exporter = AVAssetExportSession.init(asset: asset, presetName: AVAssetExportPresetMediumQuality)
        exporter?.outputFileType = AVFileTypeQuickTimeMovie
        exporter?.outputURL = outputPath
        exporter?.videoComposition = videoComposition
        
        exporter?.exportAsynchronously() { handler -> Void in
            if exporter?.status == .completed {
                print("Export complete")
                DispatchQueue.main.async(execute: {
                    completion(outputPath)
                })
                return
            } else if exporter?.status == .failed {
                print("Export failed - \(String(describing: exporter?.error))")
            }
            completion(nil)
            return
        }
    }
    

    I hope this code will work for you.