Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" {Error Domain=NSOSStatusErrorDomain Code=-16976 "(null)"}

I am working on Video application in Swift3 iOS. Basically I have to merged the Video Assets and Audios into one with Fade Effect and save this to iPhone gallery. To achieve this, I am using below method:

private func doMerge(arrayVideos:[AVAsset], arrayAudios:[AVAsset], animation:Bool, completion:@escaping Completion) -> Void {

        var insertTime = kCMTimeZero
        var audioInsertTime = kCMTimeZero
        var arrayLayerInstructions:[AVMutableVideoCompositionLayerInstruction] = []
        var outputSize = CGSize.init(width: 0, height: 0)

        // Determine video output size
        for videoAsset in arrayVideos {
            let videoTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0]

            let assetInfo = orientationFromTransform(transform: videoTrack.preferredTransform)
            var videoSize = videoTrack.naturalSize
            if assetInfo.isPortrait == true {
                videoSize.width = videoTrack.naturalSize.height
                videoSize.height = videoTrack.naturalSize.width
            }
            outputSize = videoSize
        }

        // Init composition
        let mixComposition = AVMutableComposition.init()

        for index in 0..<arrayVideos.count {
            // Get video track
            guard let videoTrack = arrayVideos[index].tracks(withMediaType: AVMediaTypeVideo).first else { continue }

            // Get audio track
            var audioTrack:AVAssetTrack?
            if index < arrayAudios.count {
                if arrayAudios[index].tracks(withMediaType: AVMediaTypeAudio).count > 0 {
                    audioTrack = arrayAudios[index].tracks(withMediaType: AVMediaTypeAudio).first
                }
            }
            // Init video & audio composition track
            let videoCompositionTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))

            let audioCompositionTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))

            do {
                let startTime = kCMTimeZero
                let duration = arrayVideos[index].duration

                // Add video track to video composition at specific time
                try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(startTime, duration), of: videoTrack, at: insertTime)

                // Add audio track to audio composition at specific time
                var audioDuration = kCMTimeZero
                if index < arrayAudios.count   {
                    audioDuration = arrayAudios[index].duration
                }

                if let audioTrack = audioTrack {
                    do {
                        try audioCompositionTrack.insertTimeRange(CMTimeRangeMake(startTime, audioDuration), of: audioTrack, at: audioInsertTime)
                    }
                    catch {
                        print(error.localizedDescription)
                    }
                }

                // Add instruction for video track
                let layerInstruction = videoCompositionInstructionForTrack(track: videoCompositionTrack, asset: arrayVideos[index], standardSize: outputSize, atTime: insertTime)

                // Hide video track before changing to new track
                let endTime = CMTimeAdd(insertTime, duration)

                if animation {
                    let timeScale = arrayVideos[index].duration.timescale
                    let durationAnimation = CMTime.init(seconds: 1, preferredTimescale: timeScale)

                    layerInstruction.setOpacityRamp (fromStartOpacity: 1.0, toEndOpacity: 0.0, timeRange: CMTimeRange.init(start: endTime, duration: durationAnimation))
                }
                else {
                    layerInstruction.setOpacity(0, at: endTime)
                }

                arrayLayerInstructions.append(layerInstruction)

                // Increase the insert time
                audioInsertTime = CMTimeAdd(audioInsertTime, audioDuration)
                insertTime = CMTimeAdd(insertTime, duration)
            }
            catch {
                print("Load track error")
            }
        }

        // Main video composition instruction
        let mainInstruction = AVMutableVideoCompositionInstruction()
        mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, insertTime)
        mainInstruction.layerInstructions = arrayLayerInstructions

        // Main video composition
        let mainComposition = AVMutableVideoComposition()
        mainComposition.instructions = [mainInstruction]
        mainComposition.frameDuration = CMTimeMake(1, 30)
        mainComposition.renderSize = outputSize

        // Export to file
        let path = NSTemporaryDirectory().appending("mergedVideo.mp4")
        let exportURL = URL.init(fileURLWithPath: path)

        // Remove file if existed
        FileManager.default.removeItemIfExisted(exportURL)

        // Init exporter
        let exporter = AVAssetExportSession.init(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
        exporter?.outputURL = exportURL
        exporter?.outputFileType = AVFileTypeQuickTimeMovie//AVFileType.mp4
        exporter?.shouldOptimizeForNetworkUse = false //true
        exporter?.videoComposition = mainComposition

        // Do export
        exporter?.exportAsynchronously(completionHandler: {
            DispatchQueue.main.async {
                self.exportDidFinish(exporter: exporter, videoURL: exportURL, completion: completion)
            }
        })

    }



fileprivate func exportDidFinish(exporter:AVAssetExportSession?, videoURL:URL, completion:@escaping Completion) -> Void {
        if exporter?.status == AVAssetExportSessionStatus.completed {
            print("Exported file: \(videoURL.absoluteString)")
            completion(videoURL,nil)
        }
        else if exporter?.status == AVAssetExportSessionStatus.failed {
            completion(videoURL,exporter?.error)

            print(exporter?.error as Any)
        }
    }

Problem: In my exportDidFinish method, AVAssetExportSessionStatus is getting failed with below error message:

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

Can anyone suggest me on this.

like image 239
user2786 Avatar asked Jul 09 '18 06:07

user2786


1 Answers

I had the exact same error and only on the iPhone 5S simulator running iOS11. I fixed it by changing the quality setting on the export operation from "Highest" (AVAssetExportPresetHighestQuality) to "Pass through" (AVAssetExportPresetPassthrough) (keeping original quality):

/// try to start an export session and set the path and file type
    if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetPassthrough) { /* AVAssetExportPresetHighestQuality */
      exportSession.outputURL = videoOutputURL
      exportSession.outputFileType = AVFileType.mp4
      exportSession.shouldOptimizeForNetworkUse = true

      exportSession.exportAsynchronously(completionHandler: {
        switch exportSession.status {
        case .failed:
          if let _error = exportSession.error {
            // !!!used to fail over here with 11800, -16976 codes, if using AVAssetExportPresetHighestQuality.  But works fine when using:  AVAssetExportPresetPassthrough
            failure(_error)
          }
          ....

Hope this helps someone, because that error code and message doesn't provide any information. It's just an "Unknown error". Besides changing the quality setting, I would try changing other settings and stripping down the export operation to identify a specific component of that operation that may be failing. (Some specific image, audio or video asset). When you have such a general error message, it's good to use the process of elimination, cutting the code in half each time, to get to the problem in Logarithmic time.

like image 140
FranticRock Avatar answered Oct 13 '22 15:10

FranticRock