Swift AVFoundation instructions don't set the opacity

90 Views Asked by At

I have a function that merges videos. All videos merge properly and the first two videos will play perfectly but then only the audio for the third video plays. I am assuming the video is there, but it is just blocked by the second video. I am confused though because I am using instructions to set the opacity of each asset to 0 when the video is done, but it doesn't work. Also even if I don't set any instructions, the first video disappears to allow the second video to play, but the second video never disappears. What is going on!?

    func mergeVideo(completion: @escaping (_ url: URL?, _ error: Error?) -> Void) {
        let mixComposition = AVMutableComposition()
        let videoComposition = AVMutableVideoComposition()

        guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
            completion(nil, nil)
            return
        }
        
        let outputURL = documentDirectory.appendingPathComponent("\(id).mov")
        
        do {
            if FileManager.default.fileExists(atPath: outputURL.path) {
                try FileManager.default.removeItem(at: outputURL)
            }
        } catch {
            print(error.localizedDescription)
        }
        
        // If there is only one video, save export time.
        if let video = videos.first, videos.count == 1 {
            do {
                if let url = URL(string: video.videoURL) {
                    try FileManager().copyItem(at: url, to: outputURL)
                    completion(outputURL, nil)
                    mergedVideoURL = outputURL.lastPathComponent
                }
            } catch let error {
                completion(nil, error)
            }
            return
        }
        
        var currentTime = CMTime.zero
        let renderSize = CGSize(width: 1280.0, height: 720.0)
        let mainInstruction = AVMutableVideoCompositionInstruction()

        videos.enumerated().forEach { index, video in
            if let vidURL = URL(string: video.videoURL)?.lastPathComponent {
                let url = documentDirectory.appendingPathComponent(vidURL)
                let asset = AVAsset(url: url)

                guard let assetTrack = asset.tracks.first else { return }

                mainInstruction.timeRange = CMTimeRangeMake(start: .zero, duration: CMTimeAdd(mixComposition.duration, asset.duration))
                let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: assetTrack)
                instruction.setOpacity(0.0, at: asset.duration)
                mainInstruction.layerInstructions.append(instruction)
                          
                do {
                    let timeRange = CMTimeRangeMake(start: .zero, duration: asset.duration)
                    try mixComposition.insertTimeRange(timeRange, of: asset, at: currentTime)
                    currentTime = CMTimeAdd(currentTime, asset.duration)
                } catch let error {
                    completion(nil, error)
                }

            }
        }//forEach

        videoComposition.instructions = [mainInstruction]
        videoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
        videoComposition.renderSize = renderSize
        
        guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetPassthrough) else {
            completion(nil, nil)
            return
        }
        exporter.outputURL = outputURL
        exporter.outputFileType = .mov
        // Pass Video Composition to the Exporter.
        exporter.videoComposition = videoComposition
        
        exporter.exportAsynchronously {
            DispatchQueue.main.async {
                switch exporter.status {
                case .completed:
                    completion(exporter.outputURL, nil)
                case .failed:
                    completion(exporter.outputURL, exporter.error)
                case.cancelled:
                    completion(exporter.outputURL, exporter.error)
                case .unknown:
                    completion(exporter.outputURL, exporter.error)
                case .waiting:
                    print("waiting")
                case .exporting:
                    print("exporting")
                @unknown default:
                    completion(exporter.outputURL, exporter.error)
                }
            }
        }
    }
0

There are 0 best solutions below