我正在创建一个应用程序,我需要录制视频并将其上传到服务器。现在我的项目也有一个Android版本。为了支持Android版本,我必须以mp4格式录制视频。我按照this教程将UIImagePicker媒体类型设置为电影格式imagePicker.mediaTypes = [kUTTypeMovie as String]
UIImagePickerController
非常适合我的要求,我唯一需要改变的是它的保存格式为mp4。我在kUTTypeMPEG4
中尝试mediaTypes
,但它在运行时抛出错误,没有错误描述。
这是我的视频捕捉功能
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格式直接录制视频。只能从apple的quicktime mov格式转换为所需的格式。
答案 0 :(得分:12)
我对以下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).")
}
}
答案 1 :(得分:9)
以下是一些可用于将录制的视频转换为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__).")
}
}
答案 2 :(得分:4)
快速快速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)
}
}
答案 3 :(得分:4)
Swift 5.2 更新解决方案
// Don't forget to import AVKit
func encodeVideo(at videoURL: URL, completionHandler: ((URL?, Error?) -> Void)?) {
let avAsset = AVURLAsset(url: videoURL, options: nil)
let startDate = Date()
//Create Export session
guard let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) else {
completionHandler?(nil, nil)
return
}
//Creating temp path to save the converted video
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
let filePath = documentsDirectory.appendingPathComponent("rendered-Video.mp4")
//Check if the file already exists then remove the previous file
if FileManager.default.fileExists(atPath: filePath.path) {
do {
try FileManager.default.removeItem(at: filePath)
} catch {
completionHandler?(nil, error)
}
}
exportSession.outputURL = filePath
exportSession.outputFileType = AVFileType.mp4
exportSession.shouldOptimizeForNetworkUse = true
let start = CMTimeMakeWithSeconds(0.0, preferredTimescale: 0)
let range = CMTimeRangeMake(start: start, duration: avAsset.duration)
exportSession.timeRange = range
exportSession.exportAsynchronously(completionHandler: {() -> Void in
switch exportSession.status {
case .failed:
print(exportSession.error ?? "NO ERROR")
completionHandler?(nil, exportSession.error)
case .cancelled:
print("Export canceled")
completionHandler?(nil, nil)
case .completed:
//Video conversion finished
let endDate = Date()
let time = endDate.timeIntervalSince(startDate)
print(time)
print("Successful!")
print(exportSession.outputURL ?? "NO OUTPUT URL")
completionHandler?(exportSession.outputURL, nil)
default: break
}
})
}
答案 4 :(得分:1)
先前示例的次要重构:
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上播放视频时有效。
P.S。请注意,exportVideo
方法的完成处理程序可能不会在主线程上返回。
答案 5 :(得分:0)
在iOS11上运行,我们将始终收到AVAssetExportSession的nil值。我们对这个案子有什么解决方案吗?
if let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) {
//work on iOS 9 and 10
} else {
//always on iOS 11
}