Play video synthesized by AVMutableComposition in AVPlayer with error

383 Views Asked by At

I am creating an IOS video editing tool by AVMutableComposition. If I combine multiple videos in a single video mutabletrack, it works fine. However, when I create mutabletrack per video and synthesize the video and play it in AVPlayer, only sound plays and video stops when move on the next video. However, if I seek video, it works normally. What's the cause?

The following is the code in which the current situation occur.

    static func mergeClips(
        videos: [ExCAVideoLayer]?,
        audios: [ExCAAudioLayer]? = nil
    ) -> AVPlayerItem? {
        guard let videos = videos else {
            return nil
        }

        let mixComposition = AVMutableComposition()
        
        var layerInstructions = [AVMutableVideoCompositionLayerInstruction]()
        
        do {
            // Determind video aspect ratio
            var aspectRatio: Double = 0.00001
            for video in videos {
                guard let url = video.videoUrl else {
                    continue
                }
                
                if let naturalSize = self.resolutionForLocalVideo(url: url) {
                    let naturalAspectRatio = naturalSize.height == 0 ? 1 : naturalSize.width / naturalSize.height
                    if aspectRatio < naturalAspectRatio {
                        aspectRatio = naturalAspectRatio
                    }
                }
            }

            
            var frameWidth = UIScreen.main.bounds.width
            var frameHeight = round(UIScreen.main.bounds.width / aspectRatio)
            
            if frameWidth.truncatingRemainder(dividingBy: 2)  > 0 {
                frameWidth = frameWidth - 1
            }

            if frameHeight.truncatingRemainder(dividingBy: 2) > 0 {
                frameHeight = frameHeight - 1
            }
            
            let frameSize = CGSize(width: frameWidth, height: frameHeight)
            
            // Video composition
            var currentDuration = mixComposition.duration
            

            
            for video in videos {
                guard let url = video.videoUrl else {
                    continue
                }
                let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
                let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
                let avAsset = AVAsset(url: url)
                
                if let layerInstruction = self.videoCompositionInstruction(videoTrack, asset: avAsset, frameSize: frameSize) {
                    layerInstructions.append(layerInstruction)
                }
                
                let streamRange = CMTimeRangeMake(start: CMTime.zero, duration: CMTimeAdd(avAsset.duration, CMTimeMakeWithSeconds(video.gap, preferredTimescale: 1)))
                
                if let streamVideoTrack = avAsset.tracks(withMediaType: .video).first {
                    try videoTrack?.insertTimeRange(streamRange, of: streamVideoTrack, at: currentDuration)
                    videoTrack?.preferredTransform = streamVideoTrack.preferredTransform
                }
                
                if let streamAudioTrack = avAsset.tracks(withMediaType: .audio).first {
                    try audioTrack?.insertTimeRange(streamRange, of: streamAudioTrack, at: currentDuration)
                }

                currentDuration = mixComposition.duration
            }
            
            // Audio composition
            if let audios = audios {
                for audio in audios {
                    guard let url = audio.audioUrl else {
                        continue
                    }

                    let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)

                    let avAsset = AVAsset(url: url)
                    let streamRange = CMTimeRangeMake(start: CMTime.zero, duration: avAsset.duration)

                    if let streamAudioTrack = avAsset.tracks(withMediaType: .audio).first {
                        try audioTrack?.insertTimeRange(streamRange, of: streamAudioTrack, at: CMTimeMakeWithSeconds(audio.startTime, preferredTimescale: 1))
                    }
                }
            }
            
            
            //AVVideoComposition and layout
            let videoComposition = AVMutableVideoComposition()
             
            videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
            videoComposition.renderSize = CGSize(width: frameSize.width, height: frameSize.height)
             
            let mainInstruction  = AVMutableVideoCompositionInstruction()
            mainInstruction .timeRange = CMTimeRange(start: .zero, duration: mixComposition.duration)
            mainInstruction.backgroundColor = UIColor.black.cgColor

            mainInstruction.layerInstructions = layerInstructions
            videoComposition.instructions = [mainInstruction]
             
            let assetKeys = [
                "playable",
                "hasProtectedContent"
            ]
            
            let avPlayItem = AVPlayerItem(asset: mixComposition, automaticallyLoadedAssetKeys: assetKeys)
            avPlayItem.videoComposition = videoComposition
            return avPlayItem
        }
        catch(let error) {
            print("Could not create mixComposition \(error.localizedDescription)")
            return nil
        }
    }

    func PlayVideo() {
        guard let avPlayerItem = VideoComposition.mergeClips(videos: self.projectRender?.videoItems) else {
            return
        }

        self.avPlayer = AVPlayer(playerItem: avPlayerItem)
        self.avPlayerLayer = AVPlayerLayer(player: self.avPlayer)
        self.avPlayerLayer?.needsDisplayOnBoundsChange = true
        if let avPlayerLayer = self.avPlayerLayer {
            self.videoContainer?.layer.addSublayer(avPlayerLayer)
        }
        
        self.avPlayer?.play()
    }
0

There are 0 best solutions below