Crop video swift

前端 未结 2 825
时光说笑
时光说笑 2021-01-03 06:40

I am recording a video in a square UIView but when I export the video is full screen 1080x1920 now I am wondering how I can reduce the video from being full screen to being

相关标签:
2条回答
  • 2021-01-03 07:27
    func cropFrame(videoAsset:AVAsset, animation:Bool) -> Void {
        var insertTime = kCMTimeZero
        var arrayLayerInstructions:[AVMutableVideoCompositionLayerInstruction] = []
        var outputSize = CGSize.init(width: 0, height: 0)
    
        // Determine video output size
        let videoTrack = videoAsset.tracks(withMediaType: AVMediaType.video)[0]
    
        let assetInfo = orientationFromTransform(transform: videoTrack.preferredTransform)
    
        var videoSize = videoTrack.naturalSize
        if assetInfo.isPortrait == true {
            videoSize.width = videoTrack.naturalSize.height
            videoSize.height = videoTrack.naturalSize.width
        }
    
        if videoSize.height > outputSize.height {
            outputSize = videoSize
        }
    
        let defaultSize = CGSize(width: 1920, height: 1080) // Default video size
    
        if outputSize.width == 0 || outputSize.height == 0 {
            outputSize = defaultSize
        }
    
        // Silence sound (in case of video has no sound track)
        let silenceURL = Bundle.main.url(forResource: "silence", withExtension: "mp3")
        let silenceAsset = AVAsset(url:silenceURL!)
        let silenceSoundTrack = silenceAsset.tracks(withMediaType: AVMediaType.audio).first
    
        // Init composition
        let mixComposition = AVMutableComposition.init()
    
    
        // Get audio track
        var audioTrack:AVAssetTrack?
        if videoAsset.tracks(withMediaType: AVMediaType.audio).count > 0 {
            audioTrack = videoAsset.tracks(withMediaType: AVMediaType.audio).first
        }
        else {
            audioTrack = silenceSoundTrack
        }
    
        // Init video & audio composition track
        let videoCompositionTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video,
                                                                   preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
    
        let audioCompositionTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio,
                                                                   preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
    
        do {
            let startTime = kCMTimeZero
            let duration = videoAsset.duration
    
            // Add video track to video composition at specific time
            try videoCompositionTrack?.insertTimeRange(CMTimeRangeMake(startTime, duration),
                                                       of: videoTrack,
                                                       at: insertTime)
    
            // Add audio track to audio composition at specific time
            if let audioTrack = audioTrack {
                try audioCompositionTrack?.insertTimeRange(CMTimeRangeMake(startTime, duration),
                                                           of: audioTrack,
                                                           at: insertTime)
            }
    
            // Add instruction for video track
            let layerInstruction = videoCompositionInstructionForTrack(track: videoCompositionTrack!,
                                                                       asset: videoAsset,
                                                                       standardSize: outputSize,
                                                                       atTime: insertTime)
            // Hide video track before changing to new track
            let endTime = CMTimeAdd(insertTime, duration)
            //let finalTimer = CMTimeAdd(CMTime(seconds: 5, preferredTimescale: videoAsset.duration.timescale), CMTime(seconds: 5, preferredTimescale: videoAsset.duration.timescale))
    
            //Kalpesh crop video frames
            if animation {
                let timeScale = videoAsset.duration.timescale
                let durationAnimation = CMTime.init(seconds: 1, preferredTimescale: timeScale)
                layerInstruction.setOpacityRamp(fromStartOpacity: 1.0, toEndOpacity: 0.0, timeRange: CMTimeRange.init(start: endTime, duration: durationAnimation))
    
                //**********======== CROP YOUR VIDEO FRAME HERE MANUALLY ========**********
    
                layerInstruction.setCropRectangle(CGRect(x: 0, y: 0, width: videoTrack.naturalSize.width, height: 300.0), at: startTime)
            } else {
                layerInstruction.setOpacity(0, at: endTime)
            }
            arrayLayerInstructions.append(layerInstruction)
    
            // Increase the insert time
            insertTime = CMTimeAdd(insertTime, duration)
        }
        catch {
            print("Load track error")
        }
    
    
        // Main video composition instruction
        let mainInstruction = AVMutableVideoCompositionInstruction()
        mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, insertTime)
        mainInstruction.layerInstructions = arrayLayerInstructions
    
        // Main video composition
        let mainComposition = AVMutableVideoComposition()
        mainComposition.instructions = [mainInstruction]
        mainComposition.frameDuration = CMTimeMake(1, 30)
        mainComposition.renderSize = outputSize
    
        // Export to file
        let path = NSTemporaryDirectory().appending("mergedVideo.mp4")
        let exportURL = URL.init(fileURLWithPath: path)
    
    
        // Init exporter
        let exporter = AVAssetExportSession.init(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
        exporter?.outputURL = exportURL
        exporter?.outputFileType = AVFileType.mp4
        exporter?.shouldOptimizeForNetworkUse = true
        exporter?.videoComposition = mainComposition
    
        // Do export
        exporter?.exportAsynchronously(completionHandler: {
    
        })
    }
    
    
    func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset, standardSize:CGSize, atTime: CMTime) -> AVMutableVideoCompositionLayerInstruction {
        let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
        let assetTrack = asset.tracks(withMediaType: AVMediaType.video)[0]
    
        let transform = assetTrack.preferredTransform
        let assetInfo = orientationFromTransform(transform: transform)
    
        var aspectFillRatio:CGFloat = 1
        if assetTrack.naturalSize.height < assetTrack.naturalSize.width {
            aspectFillRatio = standardSize.height / assetTrack.naturalSize.height
        }
        else {
            aspectFillRatio = standardSize.width / assetTrack.naturalSize.width
        }
    
        if assetInfo.isPortrait {
            let scaleFactor = CGAffineTransform(scaleX: aspectFillRatio, y: aspectFillRatio)
    
            let posX = standardSize.width/2 - (assetTrack.naturalSize.height * aspectFillRatio)/2
            let posY = standardSize.height/2 - (assetTrack.naturalSize.width * aspectFillRatio)/2
            let moveFactor = CGAffineTransform(translationX: posX, y: posY)
    
            instruction.setTransform(assetTrack.preferredTransform.concatenating(scaleFactor).concatenating(moveFactor), at: atTime)
    
        } else {
            let scaleFactor = CGAffineTransform(scaleX: aspectFillRatio, y: aspectFillRatio)
    
            let posX = standardSize.width/2 - (assetTrack.naturalSize.width * aspectFillRatio)/2
            let posY = standardSize.height/2 - (assetTrack.naturalSize.height * aspectFillRatio)/2
            let moveFactor = CGAffineTransform(translationX: posX, y: posY)
    
            var concat = assetTrack.preferredTransform.concatenating(scaleFactor).concatenating(moveFactor)
    
            if assetInfo.orientation == .down {
                let fixUpsideDown = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
                concat = fixUpsideDown.concatenating(scaleFactor).concatenating(moveFactor)
            }
    
            instruction.setTransform(concat, at: atTime)
        }
        return instruction
    }
    func orientationFromTransform(transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) {
        var assetOrientation = UIImageOrientation.up
        var isPortrait = false
        if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {
            assetOrientation = .right
            isPortrait = true
        } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {
            assetOrientation = .left
            isPortrait = true
        } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 {
            assetOrientation = .up
        } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 {
            assetOrientation = .down
        }
        return (assetOrientation, isPortrait)
    }
    

    Call this way:

        let avssets = AVAsset(url: VideoURL)
        self.cropFrame(videoAsset: avssets, animation: true)
    
    0 讨论(0)
  • 2021-01-03 07:44

    Firstly you need to make use of the AVCaptureFileOutputRecordingDelegate.

    You specifically use the func capture( _ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error! ) method to perform the cropping process, once the video has finished recording.

    Here is an example of a cropping function I once implemented. You need to pass in the URL of the video that was recorded and a callback that is used to return the new URL of the cropped video once the cropping process is finished.

       func cropVideo( _ outputFileUrl: URL, callback: @escaping ( _ newUrl: URL ) -> () )
       {
           // Get input clip
           let videoAsset: AVAsset = AVAsset( url: outputFileUrl )
           let clipVideoTrack = videoAsset.tracks( withMediaType: AVMediaTypeVideo ).first! as AVAssetTrack
    
           // Make video to square
           let videoComposition = AVMutableVideoComposition()
           videoComposition.renderSize = CGSize( width: clipVideoTrack.naturalSize.height, height: clipVideoTrack.naturalSize.height )
           videoComposition.frameDuration = CMTimeMake( 1, self.framesPerSecond )
    
           // Rotate to portrait
           let transformer = AVMutableVideoCompositionLayerInstruction( assetTrack: clipVideoTrack )
           let transform1 = CGAffineTransform( translationX: clipVideoTrack.naturalSize.height, y: -( clipVideoTrack.naturalSize.width - clipVideoTrack.naturalSize.height ) / 2 )
           let transform2 = transform1.rotated(by: CGFloat( M_PI_2 ) )
           transformer.setTransform( transform2, at: kCMTimeZero)
    
           let instruction = AVMutableVideoCompositionInstruction()
           instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds( self.intendedVideoLength, self.framesPerSecond ) )
    
           instruction.layerInstructions = [transformer]
           videoComposition.instructions = [instruction]
    
           // Export
           let croppedOutputFileUrl = URL( fileURLWithPath: FileManager.getOutputPath( String.random() ) )
           let exporter = AVAssetExportSession(asset: videoAsset, presetName: AVAssetExportPresetHighestQuality)!
           exporter.videoComposition = videoComposition
           exporter.outputURL = croppedOutputFileUrl
           exporter.outputFileType = AVFileTypeQuickTimeMovie
    
           exporter.exportAsynchronously( completionHandler: { () -> Void in
               DispatchQueue.main.async(execute: {
                   callback( croppedOutputFileUrl )
               })
           })
       }
    

    Also, here is the implementation of my getOutputPath method:

    func getOutputPath( _ name: String ) -> String
    {
        let documentPath = NSSearchPathForDirectoriesInDomains(      .documentDirectory, .userDomainMask, true )[ 0 ] as NSString
        let outputPath = "\(documentPath)/\(name).mov"
        return outputPath
    }
    

    Hope this helps.

    0 讨论(0)
提交回复
热议问题