Search code examples
swiftvideo-processingavassetexportsessionavcapturemoviefileoutput

How to crop a video recorded given by AVCaptureMovieFileOutput?


I've made CustomCamera which records video into full screen (by setting AVCaptureVideoPreviewLayer's videoGravity to resizeAspectFill) and saving the recording using AVCaptureMovieFileOutput. The recording works perfect but when I save the recorded video into Gallery it is not played in full screen. It has black bar at top and bottom. So, while recording it is shown in full screen due to resizeAspectFill but after exporting the video is exported without any transformation so it is played in the ratio it was recorded (I'm guessing). The code that I've tried to make the video export full screen is below but it is not working.

func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
if let err = error as NSError?, err.code != -11810 { //-11810 = recording stopped when timelimit reached
    print("Error recording movie: \(err)")
} else {
    let videoAsset: AVAsset = AVAsset( url: outputFileURL )
    guard let clipVideoTrack = videoAsset.tracks( withMediaType: AVMediaType.video ).first as AVAssetTrack? else {
        
        return
    }
    
    let videoComposition = AVMutableVideoComposition(propertiesOf: videoAsset)

    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = clipVideoTrack.timeRange
    
     let transformer = AVMutableVideoCompositionLayerInstruction( assetTrack: clipVideoTrack)
    transformer.setTransform(videoPreviewLayer.affineTransform(), at: .zero) // This is the transform value of AVCaptureVideoPreviewLayer
    print("clipVideoTrack size: \(clipVideoTrack.naturalSize)")
    
    instruction.layerInstructions = [transformer]
    videoComposition.instructions = [instruction]
    
    let outputPath = "\(NSTemporaryDirectory())\(UUID().uuidString).mov"
    let croppedOutputFileUrl = URL(fileURLWithPath: outputPath)
    
    let exporter = AVAssetExportSession(asset: videoAsset, presetName: AVAssetExportPresetHighestQuality)!
    exporter.videoComposition = videoComposition
    exporter.outputURL = croppedOutputFileUrl
    exporter.outputFileType = .mov
    exporter.shouldOptimizeForNetworkUse = true
    exporter.canPerformMultiplePassesOverSourceMediaData = true
    
    exporter.exportAsynchronously( completionHandler: { () -> Void in
        if let error = exporter.error {
            print("Error in exporting: \(error)")
        }
        if exporter.status == .completed {
            print("exported successfully")
        }
        DispatchQueue.main.async(execute: {
            if let url = exporter.outputURL {
                print("exported file url: \(url)")
            } else {
            }
            
        })
    })
}
}

Solution

  • I found out that using the AVCaptureMovieFileOutput it is hard to change the resolution of the recorded video and cropping comes with alot of challenges. So, finally I decided to give AVCaptureVideoDataOutput a try and it worked like a charm. I'm able to record the video in fullscreen as well as when the AVCaptureVideoDataOutput produces the final video it looks exactly same while video recording is going on.