azrosen92 azrosen92 - 21 days ago 13
Swift Question

Saving Videos with AVMutableComposition and AVAssetExportSession in Swift 3

I'm attempting to build a small toy iOS app that merges two video assets from the user's photos library. I've gotten to the point where I have merged the videos using an

AVMutableComposition
instance and now I need to export the composition. I am doing so with the following code:

func saveEditedComposition(_ composition: AVMutableComposition) {
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let savePath = (documentDirectory as NSString).strings(byAppendingPaths: ["mergeVideo.mov"])[0]
let url = NSURL(fileURLWithPath: savePath)

// Set up exporter
guard let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else { return }
exporter.outputURL = url as URL
exporter.outputFileType = AVFileTypeQuickTimeMovie
exporter.shouldOptimizeForNetworkUse = true

// Perform the export
exporter.exportAsynchronously(completionHandler: { () -> Void in
// Upon completion of the export,
DispatchQueue.global().async {
if exporter.status == .completed {
let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [exporter.outputURL!], options: nil))
let phAsset = fetchResult.firstObject! // Crashes here, returning nil.
}
}
})
}


The problem is, when the completionHandler is run, I am able to see that my exporter has completed without error (
if exporter.status == .completed {
), but when I try to access the asset at
exporter.outputURL
, it returns an empty
PHFetchResult<PHAsset>
. Can anyone see what I'm doing wrong here?

Answer

You're exporting the video to the file system (the Documents directory), yet fetching from the camera roll/ALAssetsLibrary.

It depends on what you need to do with the exported file. If it needs to be in the ALAssetsLibrary, you can use

ALAssetsLibrary().writeVideoAtPath(toSavedPhotosAlbum: exporter.outputURL!) { alAssetURL, error in      
}

However ALAssetsLibrary is now deprecated and you are supposed to use PHPhotoLibrary. I don't know the Photos framework, but I think it works like this:

var placeHolder: PHObjectPlaceholder?

PHPhotoLibrary.shared().performChanges({
    let changeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: exporter.outputURL!)
    if let changeRequest = changeRequest {
        // maybe set date, location & favouriteness here?
        placeHolder = changeRequest.placeholderForCreatedAsset
    }
}) { success, error in
    placeHolder?.localIdentifier    // should identify asset from now on?
}

If you don't need the video to live in the camera roll, then you can work directly with the file in exporter.outputURL!.

Comments