Oleg Gordiichuk
Oleg Gordiichuk

Reputation: 15512

AVAssetExportSession issue with AVAssetExportPreset type

I am using this extension to save video file from AVAsset to the tmp folder. Problem is that when I am using AVAssetExportPresetHighestQuality type video files could not be saved due to that reason:

Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSUnderlyingError=0x1748482e0 {Error Domain=NSOSStatusErrorDomain Code=-12780 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed}

Also some times even when I am using AVAssetExportPresetHighestQuality it saves video but in random order.

extension AVAsset {

    func write(to url: URL, success: @escaping () -> (), failure: @escaping (Error) -> ()) {
        guard let exportSession = AVAssetExportSession(asset: self, presetName: AVAssetExportPresetMediumQuality) else {
            let error = NSError(domain: "domain", code: 0, userInfo: nil)
            failure(error)

            return
        }

        exportSession.outputFileType = AVFileTypeMPEG4
        exportSession.outputURL = url

        exportSession.exportAsynchronously {
            switch exportSession.status {
            case .completed:
                success()
            case .unknown, .waiting, .exporting, .failed, .cancelled:
                let error = NSError(domain: "domain", code: 0, userInfo: nil)
                failure(error)
            }
        }
    }
}

Upvotes: 3

Views: 1596

Answers (1)

Oleg Gordiichuk
Oleg Gordiichuk

Reputation: 15512

This issue is connected with wrong length of the AVAsset components. For some reason AVAsset tracks have different duration of the video and audio tracks and it was the main issue.

To solve this issue I am using custom extension of the AVAsset.This function will create new AVAsset based on video and audio tracks with condition that will fix duration issue. So AVAsset obtained from normalizingMediaDuration() could be successfully exported.

extension AVAsset {
    func normalizingMediaDuration() -> AVAsset? {
        let mixComposition : AVMutableComposition = AVMutableComposition()
        var mutableCompositionVideoTrack : [AVMutableCompositionTrack] = []
        var mutableCompositionAudioTrack : [AVMutableCompositionTrack] = []
        let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()

        guard let video = tracks(withMediaType: AVMediaTypeVideo).first else {
            return nil
        }

        guard let audio = tracks(withMediaType: AVMediaTypeAudio).first else {
            return nil
        }

        mutableCompositionVideoTrack.append(mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid))
        mutableCompositionAudioTrack.append(mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid))

        let duration = video.timeRange.duration.seconds > audio.timeRange.duration.seconds ? audio.timeRange.duration : video.timeRange.duration

        do{
            try mutableCompositionVideoTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero,duration), of: video, at: kCMTimeZero)
            try mutableCompositionAudioTrack[0].insertTimeRange(CMTimeRangeMake(kCMTimeZero, duration), of: audio, at: kCMTimeZero)
        }catch{
            return nil
        }

        totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,duration)

        return mixComposition
    }
}

Upvotes: 3

Related Questions