webentwicklung-frage-antwort-db.com.de

Swift - Wie zeichnet man mit UIImagePickerController Videos im MP4-Format auf?

Ich erstelle eine App, in der ich Videos aufnehmen und auf einen Server hochladen muss. Jetzt hat mein Projekt auch eine Android-Version. Um die Android-Version zu unterstützen, muss ich die Videos im mp4-Format aufnehmen. Ich folgte der Anleitung " this ", um den UIImagePicker-Medientyp auf das Filmformat zu setzen. imagePicker.mediaTypes = [kUTTypeMovie as String]

Die UIImagePickerController ist perfekt für meine Anforderungen und das einzige, was ich ändern muss, ist das Speicherformat in mp4. Ich habe kUTTypeMPEG4 in mediaTypes ausprobiert, es wird jedoch zur Laufzeit ein Fehler ohne Fehlerbeschreibung ausgegeben. 

Dies ist meine Videoaufnahmefunktion

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
    }

Ich verwende Swift 2.2 und Xcode 8 mit Use Legacy Swift Language version = Yes

Alle alternativen Lösungen werden ebenfalls geschätzt. Danke im Voraus.

BEARBEITEN: Ich habe herausgefunden, dass es keine Methode gibt, Videos direkt im mp4-Format in Swift aufzunehmen. kann nur aus dem Quicktime-Mov-Format von Apple in das erforderliche Format konvertiert werden.

11
Kautham Krishna

Hier ist ein Code, mit dem Sie das aufgezeichnete Video in MP4 konvertieren können:

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

Quelle: https://stackoverflow.com/a/39329155/4786204

8
William Taylor

Ich habe einige Änderungen an den folgenden 2 Antworten vorgenommen, um es mit kompatibel zu machen 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).")
    }
}
12
Kai-jie Ke

Ein schnelles Swift 4 Update zu den vorherigen Antworten:

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
Robert Nissl

Geringfügiges Refactoring der vorherigen Beispiele:

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

Außerdem: AVAssetExportPresetHighestQuality-Voreinstellung funktioniert, wenn ein Video unter Android/Chrome abgespielt wird.

P.S. Beachten Sie, dass der Beendigungshandler der exportVideo-Methode möglicherweise nicht im Hauptthread zurückgegeben wird.

1
tadija

Unter iOS11 werden wir immer den Nullwert für die AVAssetExportSession erhalten. Haben wir eine Lösung für diesen Fall?

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