Swift – 如何使用UIImagePickerController以MP4格式录制video?

我正在创建一个应用程序,我需要录制video并将其上传到服务器。 现在我的项目也有一个Android版本。 为了支持Android版本,我必须以mp4格式录制video。 我按照本教程将UIImagePicker媒体类型设置为电影格式imagePicker.mediaTypes = [kUTTypeMovie as String]

UIImagePickerController非常适合我的要求,我唯一需要改变的是它的mp4保存格式。 我在mediaTypes尝试了kUTTypeMPEG4 ,但它在运行时抛出错误而没有错误描述。

这是我的video拍摄function

 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 } 

我使用Swift 2.2和Xcode 8 Use Legacy swift Language version = Yes

任何替代解决方案也受到赞赏。 提前致谢。

编辑:我发现没有办法在swift中直接录制mp4格式的video。 只能从apple的quicktime mov格式转换为所需的格式。

以下是一些可用于将录制的video转换为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__).") } } 

来源: https : //stackoverflow.com/a/39329155/4786204

我对以下2个答案进行了一些修改,使其与Swift3兼容:
https://stackoverflow.com/a/40354948/2470084
https://stackoverflow.com/a/39329155/2470084

 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).") } } 

快速快速4更新以前的答案:

 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) } } 

前面例子的轻微重构:

 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) } } } 

此外: AVAssetExportPresetHighestQuality预设适用于在Android / Chrome上播放video的情况。

PS请注意,可能不会在主线程上返回exportVideo方法的完成处理程序。

在iOS11上运行,我们将始终收到AVAssetExportSession的nil值。 我们对这个案子有什么解决方案吗?

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