AVAssetExportSession AVFoundationErrorDomain Code -11800 The operation could not be completed, NSOSStatusErrorDomain Code=-12780 "(null) in Swift iOS
Asked Answered
S

1

2

I am developing a Video based Application in Swift. Where I am exporting a Video clip with Watermark logo and Fade In Out effect. Here is my code:

func watermark(video videoAsset:AVAsset, videoModal:VideoModel, watermarkText text : String!, imageName name : String!, saveToLibrary flag : Bool, watermarkPosition position : PDWatermarkPosition, withMode mode: SpeedoVideoMode, completion : ((_ status : AVAssetExportSessionStatus?, _ session: AVAssetExportSession?, _ outputURL : URL?) -> ())?) {

        var arrayLayerInstructions:[AVMutableVideoCompositionLayerInstruction] = []
        let servicemodel = ServiceModel()

        DispatchQueue.global(qos: DispatchQoS.QoSClass.default).sync {

            let mixComposition = AVMutableComposition()

            let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))

            if videoAsset.tracks(withMediaType: AVMediaTypeVideo).count == 0

            {
                completion!(nil, nil, nil)
                return
            }

            let clipVideoTrack =  videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0]

            self.addAudioTrack(composition: mixComposition, videoAsset: videoAsset as! AVURLAsset, withMode: mode, videoModal:videoModal)

            do {
                try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: clipVideoTrack, at: kCMTimeZero)

            }
            catch {
                print(error.localizedDescription)
            }

            let videoSize = clipVideoTrack.naturalSize //CGSize(width: 375, height: 300)

            //to add Watermark
            let parentLayer = CALayer()

            let videoLayer = CALayer()

            parentLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)
            videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)
            //videoLayer.backgroundColor = UIColor.red.cgColor
            parentLayer.addSublayer(videoLayer)

            if name != nil {
                let watermarkImage = UIImage(named: name)
                let imageLayer = CALayer()
                //imageLayer.backgroundColor = UIColor.purple.cgColor
                imageLayer.contents = watermarkImage?.cgImage

                var xPosition : CGFloat = 0.0
                var yPosition : CGFloat = 0.0
                let imageSize : CGFloat = 150

                switch (position) {
                case .TopLeft:
                    xPosition = 0
                    yPosition = 0
                    break
                case .TopRight:
                    xPosition = videoSize.width - imageSize - 100
                    yPosition = 80
                    break
                case .BottomLeft:
                    xPosition = 0
                    yPosition = videoSize.height - imageSize
                    break
                case .BottomRight, .Default:
                    xPosition = videoSize.width - imageSize
                    yPosition = videoSize.height - imageSize
                    break
                }

                imageLayer.frame = CGRect(x: xPosition, y: yPosition, width: imageSize, height: imageSize)
                imageLayer.opacity =  0.75
                parentLayer.addSublayer(imageLayer)

                if text != nil {
                    let titleLayer = CATextLayer()
                    titleLayer.backgroundColor = UIColor.clear.cgColor
                    titleLayer.string = text
                    titleLayer.font = "Helvetica" as CFTypeRef
                    titleLayer.fontSize = 20
                    titleLayer.alignmentMode = kCAAlignmentRight
                    titleLayer.frame = CGRect(x: 0, y: yPosition - imageSize, width: videoSize.width - imageSize/2 - 4, height: 57)
                    titleLayer.foregroundColor = UIColor.lightGray.cgColor
                    parentLayer.addSublayer(titleLayer)
                }
            }

            let videoComp = AVMutableVideoComposition()
            videoComp.renderSize = videoSize
            videoComp.frameDuration = CMTimeMake(1, 30)
            videoComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)


            let instruction = AVMutableVideoCompositionInstruction()
            instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration)

            _ = mixComposition.tracks(withMediaType: AVMediaTypeVideo)[0] as AVAssetTrack

            let layerInstruction = self.videoCompositionInstructionForTrack(track: compositionVideoTrack, asset: videoAsset)

            //Add Fade In Out effects
            let startTime = CMTime(seconds: Double(0), preferredTimescale: 1000)
            let endTime = CMTime(seconds: Double(1), preferredTimescale: 1000)
            let timeRange = CMTimeRange(start: startTime, end: endTime)
            layerInstruction.setOpacityRamp(fromStartOpacity: 0.1, toEndOpacity: 1.0, timeRange: timeRange)

            let startTime1 = CMTime(seconds: videoAsset.duration.seconds-1, preferredTimescale: 1000)
            let endTime1 = CMTime(seconds: videoAsset.duration.seconds, preferredTimescale: 1000)
            let timeRange1 = CMTimeRange(start: startTime1, end: endTime1)
            layerInstruction.setOpacityRamp(fromStartOpacity: 1.0, toEndOpacity: 0.1, timeRange: timeRange1)

            arrayLayerInstructions.append(layerInstruction)

            instruction.layerInstructions = arrayLayerInstructions
            videoComp.instructions = [instruction]

            let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]

            let url = URL(fileURLWithPath: documentDirectory).appendingPathComponent("\(videoModal.fileID).mov")

            let filePath = url.path
            let fileManager = FileManager.default

            do {
                if fileManager.fileExists(atPath: filePath) {
                    print("FILE AVAILABLE")

                    try fileManager.removeItem(atPath:filePath)
                } else {
                    print("FILE NOT AVAILABLE")
                }
            } catch _ {
            }

            let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
            exporter?.outputURL = url
            exporter?.outputFileType = AVFileTypeQuickTimeMovie

            let timeRangetoTrim = CMTimeRange(start: CMTime(seconds: Double(videoModal.leftRangeValue), preferredTimescale: 1000),
                                              end: CMTime(seconds: Double(videoModal.rightRangeValue), preferredTimescale: 1000))

            exporter?.timeRange = timeRangetoTrim

            exporter?.shouldOptimizeForNetworkUse = false
            exporter?.videoComposition = videoComp

            exporter?.exportAsynchronously() {
                DispatchQueue.main.async {

                    if exporter?.status == AVAssetExportSessionStatus.completed {

                        let outputURL = exporter?.outputURL
                        if flag {
                            if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(outputURL!.path) {
                                PHPhotoLibrary.shared().performChanges({
                                    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL!)
                                }) { saved, error in
                                    if saved {
                                        completion!(AVAssetExportSessionStatus.completed, exporter, outputURL)
                                    }
                                }
                            }

                        } else {
                            completion!(AVAssetExportSessionStatus.completed, exporter, outputURL)
                        }

                    } else {
                        // Error
                        completion!(exporter?.status, exporter, nil)// Getting error here
                    }
                }
            }
        }
    }


func addAudioTrack(composition: AVMutableComposition, videoAsset: AVURLAsset, withMode mode: SpeedoVideoMode, videoModal:VideoFileModel) {

        let compositionAudioTrack:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
        let audioTracks = videoAsset.tracks(withMediaType: AVMediaTypeAudio)
        for audioTrack in audioTracks {
            try! compositionAudioTrack.insertTimeRange(audioTrack.timeRange, of: audioTrack, at: kCMTimeZero)
        }
    }

func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction {
        let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
        let assetTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0]

        let transform = assetTrack.preferredTransform
        let assetInfo = orientationFromTransform(transform: transform)

        var scaleToFitRatio = UIScreen.main.bounds.width / 375
        if assetInfo.isPortrait {
            scaleToFitRatio = UIScreen.main.bounds.width / assetTrack.naturalSize.height
            let scaleFactor = CGAffineTransform(scaleX: scaleToFitRatio, y: scaleToFitRatio)
            instruction.setTransform(assetTrack.preferredTransform.concatenating(scaleFactor),
                                     at: kCMTimeZero)
        } else {
            let scaleFactor = CGAffineTransform(scaleX: scaleToFitRatio, y: scaleToFitRatio)
            var concat = assetTrack.preferredTransform.concatenating(scaleFactor).concatenating(CGAffineTransform(translationX: 0, y: 0))
            if assetInfo.orientation == .down {
                let fixUpsideDown = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
                let windowBounds = UIScreen.main.bounds
                let yFix = 375 + windowBounds.height
                let centerFix = CGAffineTransform(translationX: assetTrack.naturalSize.width, y: CGFloat(yFix))
                concat = fixUpsideDown.concatenating(centerFix).concatenating(scaleFactor)
            }
            instruction.setTransform(concat, at: kCMTimeZero)

        }

        return instruction
    }


private func orientationFromTransform(transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) {
        var assetOrientation = UIImageOrientation.up
        var isPortrait = false
        if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {
            assetOrientation = .right
            isPortrait = true
        } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {
            assetOrientation = .left
            isPortrait = true
        } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 {
            assetOrientation = .up
        } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 {
            assetOrientation = .down
        }

        return (assetOrientation, isPortrait)
    }

My code is working fine for some of the videos and sometimes it's not working for some videos too. I am getting below error due to AVAssetExportSessionStatus failed :

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

Can anyone help me on this? Thank you in advance.

Sada answered 8/7, 2019 at 7:57 Comment(6)
Your issue is with the Time. Video is not getting is proper time, also what this method do, can you pls post? let layerInstruction = self.videoCompositionInstructionForTrack(track: compositionVideoTrack, asset: videoAsset)? And can you pls provide a demo then also it will be helpfulWordage
Let me check and I will update you!Wordage
You again missed addAudioTrack method, can you pls send me a file without any third-party things or custom things, so that I can help!Wordage
Ohh okay, Updated now @SohilR.Memon Please checkSada
Last method orientationFromTransform, can you pls update?Wordage
Let us continue this discussion in chat.Sada
W
2

This method func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction { is wrong because you need to provide and AVAssetTrack which has the actual videos.

But instead of that, you are passing AVCompositionTrack which is still need to be composed, so replace your method with this func videoCompositionInstructionForTrack(track: AVAssetTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction {.

Now when you are calling the actual method, you need to pass clipVideoTrack, i.e., let layerInstruction = self.videoCompositionInstructionForTrack(track: clipVideoTrack, asset: videoAsset).

let me know, if you are still facing the error!

Wordage answered 8/7, 2019 at 10:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.