Swift - 如何使用UIImagePickerController以MP4格式录制视频?

问题描述 投票:11回答:5

我正在创建一个应用程序,我需要录制视频并将其上传到服务器。现在我的项目也有一个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格式转换为所需的格式。

ios swift uiimagepickercontroller video-capture
5个回答
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__).")
}
}

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


12
投票

我对以下2个答案进行了一些修改,使其与Swift 3兼容: qazxsw poi qazxsw poi

https://stackoverflow.com/a/40354948/2470084

4
投票

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

https://stackoverflow.com/a/39329155/2470084

1
投票

对前面示例的轻微重构:

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

另外:在Android / Chrome上播放视频时,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) } } } 方法的完成处理程序可能不会在主线程上返回。


0
投票

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

AVAssetExportPresetHighestQuality
© www.soinside.com 2019 - 2024. All rights reserved.