Swift - How to record MP4 video using UIImagePickerController?

I am creating an application in which I need to record a video and upload it to a server. Now my project also has an Android version. To support the Android version, I have to record videos in mp4 format. I followed this tutorial to set the UIImagePicker media type to the movie format imagePicker.mediaTypes = [kUTTypeMovie as String]

UIImagePickerController perfect for my requirement, and the only thing I need to change is its save format in mp4. I tried kUTTypeMPEG4 in mediaTypes , but it throws an error at runtime without describing the error.

This is my video capture feature.

 func startCameraFromViewController() { if UIImagePickerController.isSourceTypeAvailable(.Camera) == false { return } viewBlack.hidden = false presentViewController(cameraController, animated: false, completion: nil) cameraController.sourceType = .Camera cameraController.mediaTypes = [kUTTypeMovie as String] //cameraController.mediaTypes = [kUTTypeMPEG4 as String] cameraController.cameraCaptureMode = .Video cameraController.videoQuality = .TypeMedium if(getPurchaseId() as! Int == 0) { if(txtBenchMark.text?.isEmpty == false) { cameraController.videoMaximumDuration = NSTimeInterval(300.0) }else{ cameraController.videoMaximumDuration = NSTimeInterval(60.0) } }else{ cameraController.videoMaximumDuration = NSTimeInterval(600.0) } cameraController.allowsEditing = false } 

I am using Swift 2.2 and Xcode 8 with Use Legacy swift Language version = Yes

Any alternative solutions are also welcome. Thanks in advance.

EDIT: I found out that there is no way to directly record mp4 video in swift. only can be converted to the required format from quicktime mov format in Apple.

+10
ios swift video-capture uiimagepickercontroller
source share
5 answers

Here is the code you can use to convert the recorded video to MP4:

 func encodeVideo(videoURL: NSURL) { let avAsset = AVURLAsset(URL: videoURL, options: nil) var startDate = NSDate() //Create Export session exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) // exportSession = AVAssetExportSession(asset: composition, presetName: mp4Quality) //Creating temp path to save the converted video let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] let myDocumentPath = NSURL(fileURLWithPath: documentsDirectory).URLByAppendingPathComponent("temp.mp4").absoluteString let url = NSURL(fileURLWithPath: myDocumentPath) let documentsDirectory2 = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as NSURL let filePath = documentsDirectory2.URLByAppendingPathComponent("rendered-Video.mp4") deleteFile(filePath) //Check if the file already exists then remove the previous file if NSFileManager.defaultManager().fileExistsAtPath(myDocumentPath) { do { try NSFileManager.defaultManager().removeItemAtPath(myDocumentPath) } catch let error { print(error) } } url exportSession!.outputURL = filePath exportSession!.outputFileType = AVFileTypeMPEG4 exportSession!.shouldOptimizeForNetworkUse = true var start = CMTimeMakeWithSeconds(0.0, 0) var range = CMTimeRangeMake(start, avAsset.duration) exportSession.timeRange = range exportSession!.exportAsynchronouslyWithCompletionHandler({() -> Void in switch self.exportSession!.status { case .Failed: print("%@",self.exportSession?.error) case .Cancelled: print("Export canceled") case .Completed: //Video conversion finished var endDate = NSDate() var time = endDate.timeIntervalSinceDate(startDate) print(time) print("Successful!") print(self.exportSession.outputURL) default: break } }) } func deleteFile(filePath:NSURL) { guard NSFileManager.defaultManager().fileExistsAtPath(filePath.path!) else { return } do { try NSFileManager.defaultManager().removeItemAtPath(filePath.path!) }catch{ fatalError("Unable to delete file: \(error) : \(__FUNCTION__).") } } 

Source: stack overflow

+7
source share

I made some changes to the following 2 answers to make it compatible with Swift3 :
stack overflow
https://stackoverflow.com/questions/330237/

 import AVFoundation func encodeVideo(videoURL: URL){ let avAsset = AVURLAsset(url: videoURL) let startDate = Date() let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) let docDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] let myDocPath = NSURL(fileURLWithPath: docDir).appendingPathComponent("temp.mp4")?.absoluteString let docDir2 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL let filePath = docDir2.appendingPathComponent("rendered-Video.mp4") deleteFile(filePath!) if FileManager.default.fileExists(atPath: myDocPath!){ do{ try FileManager.default.removeItem(atPath: myDocPath!) }catch let error{ print(error) } } exportSession?.outputURL = filePath exportSession?.outputFileType = AVFileTypeMPEG4 exportSession?.shouldOptimizeForNetworkUse = true let start = CMTimeMakeWithSeconds(0.0, 0) let range = CMTimeRange(start: start, duration: avAsset.duration) exportSession?.timeRange = range exportSession!.exportAsynchronously{() -> Void in switch exportSession!.status{ case .failed: print("\(exportSession!.error!)") case .cancelled: print("Export cancelled") case .completed: let endDate = Date() let time = endDate.timeIntervalSince(startDate) print(time) print("Successful") print(exportSession?.outputURL ?? "") default: break } } } func deleteFile(_ filePath:URL) { guard FileManager.default.fileExists(atPath: filePath.path) else{ return } do { try FileManager.default.removeItem(atPath: filePath.path) }catch{ fatalError("Unable to delete file: \(error) : \(#function).") } } 
+12
source share

Quick quick update to previous answers:

 func encodeVideo(videoUrl: URL, outputUrl: URL? = nil, resultClosure: @escaping (URL?) -> Void ) { var finalOutputUrl: URL? = outputUrl if finalOutputUrl == nil { var url = videoUrl url.deletePathExtension() url.appendPathExtension(".mp4") finalOutputUrl = url } if FileManager.default.fileExists(atPath: finalOutputUrl!.path) { print("Converted file already exists \(finalOutputUrl!.path)") resultClosure(finalOutputUrl) return } let asset = AVURLAsset(url: videoUrl) if let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough) { exportSession.outputURL = finalOutputUrl! exportSession.outputFileType = AVFileType.mp4 let start = CMTimeMakeWithSeconds(0.0, 0) let range = CMTimeRangeMake(start, asset.duration) exportSession.timeRange = range exportSession.shouldOptimizeForNetworkUse = true exportSession.exportAsynchronously() { switch exportSession.status { case .failed: print("Export failed: \(exportSession.error != nil ? exportSession.error!.localizedDescription : "No Error Info")") case .cancelled: print("Export canceled") case .completed: resultClosure(finalOutputUrl!) default: break } } } else { resultClosure(nil) } } 
+4
source share

Slight refactoring of previous examples:

 import AVFoundation extension AVURLAsset { func exportVideo(presetName: String = AVAssetExportPresetHighestQuality, outputFileType: AVFileType = .mp4, fileExtension: String = "mp4", then completion: @escaping (URL?) -> Void) { let filename = url.deletingPathExtension().appendingPathExtension(fileExtension).lastPathComponent let outputURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename) if let session = AVAssetExportSession(asset: self, presetName: presetName) { session.outputURL = outputURL session.outputFileType = outputFileType let start = CMTimeMakeWithSeconds(0.0, 0) let range = CMTimeRangeMake(start, duration) session.timeRange = range session.shouldOptimizeForNetworkUse = true session.exportAsynchronously { switch session.status { case .completed: completion(outputURL) case .cancelled: debugPrint("Video export cancelled.") completion(nil) case .failed: let errorMessage = session.error?.localizedDescription ?? "n/a" debugPrint("Video export failed with error: \(errorMessage)") completion(nil) default: break } } } else { completion(nil) } } } 

Also: the AVAssetExportPresetHighestQuality preset works when the video is playing on Android / Chrome.

PS Keep in mind that the exportVideo handler of the exportVideo method cannot be returned in the main stream.

+1
source share

Running on iOS11, we will always get the nil value for AVAssetExportSession. Do we have any solution for this case?

 if let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) { //work on iOS 9 and 10 } else { //always on iOS 11 } 
0
source share

All Articles