Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - How to record video in MP4 format with UIImagePickerController?

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

The UIImagePickerController is perfect for my requirement and the only thing that i need to change is its saving format to mp4. I tried kUTTypeMPEG4 in mediaTypes but it throws error at the run time with no error description.

This is my video Capture 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
    }

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

Any Alternative Solutions are also appreciated. Thanks in advance.

EDIT: I found out that there is no method to directly record videos in mp4 format in swift. only can be converted to required format from apple's quicktime mov format.

like image 595
Kautham Krishna Avatar asked Nov 01 '16 05:11

Kautham Krishna


4 Answers

I made some modifications to the following 2 answers to make it compatible with Swift 5:
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 = AVFileType.mp4
    exportSession?.shouldOptimizeForNetworkUse = true
    
    let start = CMTimeMakeWithSeconds(0.0, preferredTimescale: 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).")
    }
}
like image 189
Kai-jie Ke Avatar answered Nov 12 '22 06:11

Kai-jie Ke


Here is some code that you can use to convert the recorded video into 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: https://stackoverflow.com/a/39329155/4786204

like image 42
William Taylor Avatar answered Nov 12 '22 06:11

William Taylor


A quick swift 4 update to the 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)
    }
}
like image 9
Robert Nissl Avatar answered Nov 12 '22 06:11

Robert Nissl


Swift 5.2 Update Solution

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

    })
}
like image 6
Sourabh Sharma Avatar answered Nov 12 '22 06:11

Sourabh Sharma