Получение первой видео дорожки несколько раз при попытке объединить три разных видео дорожки в одном кадре с помощью AVFoundation - PullRequest
0 голосов
/ 08 декабря 2018

Я хочу объединить несколько видео и их аудио в один видеокадр, для этого я использую AVFoundation framework.

Для этого я создал метод, который принимает массив ресурсов и какна данный момент я передаю три разных видео актива.

Пока что я объединил их аудио, но проблема в видео кадре, в котором видео только первого актива повторяется в каждом кадре .

Current output video

Я использую приведенный ниже код для объединения видео, которые идеально объединяют аудио всех трех видео, но первое видео во входном массиве повторяется три раза, что является основной проблемой:

Мне нужны все три разных видео в кадрах.

func merge(Videos aArrAssets: [AVAsset]){

        let mixComposition = AVMutableComposition()

        func setup(asset aAsset: AVAsset, WithComposition aComposition: AVMutableComposition) -> AVAssetTrack{

            let aMutableCompositionVideoTrack = aComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
            let aMutableCompositionAudioTrack = aComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)

            let aVideoAssetTrack: AVAssetTrack = aAsset.tracks(withMediaType: .video)[0]
            let aAudioAssetTrack: AVAssetTrack = aAsset.tracks(withMediaType: .audio)[0]

            do{
            try aMutableCompositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: aAsset.duration), of: aVideoAssetTrack, at: .zero)
            try aMutableCompositionAudioTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: aAsset.duration), of: aAudioAssetTrack, at: .zero)
            }catch{}

            return aVideoAssetTrack
        }

        let aArrVideoTracks = aArrAssets.map { setup(asset: $0, WithComposition: mixComposition) }

        var aArrLayerInstructions : [AVMutableVideoCompositionLayerInstruction] = []

        //Transform every video
        var aNewHeight : CGFloat = 0
        for (aIndex,aTrack) in aArrVideoTracks.enumerated(){

            aNewHeight += aIndex > 0 ? aArrVideoTracks[aIndex - 1].naturalSize.height : 0

            let aLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: aTrack)
            let aFristTransform = CGAffineTransform(translationX: 0, y: aNewHeight)

            aLayerInstruction.setTransform(aFristTransform, at: .zero)
            aArrLayerInstructions.append(aLayerInstruction)
        }


        let aTotalTime = aArrVideoTracks.map { $0.timeRange.duration }.max()

        let aInstruction = AVMutableVideoCompositionInstruction()
        aInstruction.timeRange = CMTimeRangeMake(start: .zero, duration: aTotalTime!)
        aInstruction.layerInstructions = aArrLayerInstructions

        let aVideoComposition = AVMutableVideoComposition()
        aVideoComposition.instructions = [aInstruction]
        aVideoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)

        let aTotalWidth = aArrVideoTracks.map { $0.naturalSize.width }.max()!
        let aTotalHeight = aArrVideoTracks.map { $0.naturalSize.height }.reduce(0){ $0 + $1 }
        aVideoComposition.renderSize = CGSize(width: aTotalWidth, height: aTotalHeight)


        saveVideo(WithAsset: mixComposition, videoComp : aVideoComposition) { (aError, aUrl) in
            print("Location : \(String(describing: aUrl))")
        }
    }

    private func saveVideo(WithAsset aAsset : AVAsset, videoComp : AVVideoComposition, completion: @escaping (_ error: Error?, _ url: URL?) -> Void){


        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "ddMMyyyy_HHmm"
        let date = dateFormatter.string(from: NSDate() as Date)

        // Exporting
        let savePathUrl: URL = URL(fileURLWithPath: NSHomeDirectory() + "/Documents/newVideo_\(date).mov")
        do { // delete old video
            try FileManager.default.removeItem(at: savePathUrl)
        } catch { print(error.localizedDescription) }


        let assetExport: AVAssetExportSession = AVAssetExportSession(asset: aAsset, presetName: AVAssetExportPresetMediumQuality)!
        assetExport.outputFileType = .mov
        assetExport.outputURL = savePathUrl
//        assetExport.shouldOptimizeForNetworkUse = true
        assetExport.videoComposition = videoComp

        assetExport.exportAsynchronously { () -> Void in
            switch assetExport.status {
            case .completed:
                print("success")
                completion(nil, savePathUrl)
            case .failed:
                print("failed \(assetExport.error?.localizedDescription ?? "error nil")")
                completion(assetExport.error, nil)
            case .cancelled:
                print("cancelled \(assetExport.error?.localizedDescription ?? "error nil")")
                completion(assetExport.error, nil)
            default:
                print("complete")
                completion(assetExport.error, nil)
            }
        }
    }

Я знаю, что делаю что-то не так в коде, но не могу понять, где мне нужна помощь, чтобы это выяснить.

Заранее спасибо.

1 Ответ

0 голосов
/ 16 декабря 2018

Ваша проблема в том, что при создании AVMutableVideoCompositionLayerInstruction ссылка aTrack является ссылкой на трек исходного актива, который вы устанавливаете с помощью

let aVideoAssetTrack: AVAssetTrack = aAsset.tracks(withMediaType: .video)[0]

Это trackID равен 1, потому что это первый трек в источнике AVAsset.Соответственно, когда вы осмотрите свой aArrLayerInstructions, вы увидите, что все идентификаторы дорожек в ваших инструкциях 1.Вот почему вы получаете первое видео три раза

(lldb) p aArrLayerInstructions[0].trackID
(CMPersistentTrackID) $R8 = 1
(lldb) p aArrLayerInstructions[1].trackID
(CMPersistentTrackID) $R10 = 1
...

Решение заключается не в перечислении исходных дорожек, а в дорожках композиции при построении инструкций слоя композиции.

let tracks = mixComposition.tracks(withMediaType: .video)
for (aIndex,aTrack) in tracks.enumerated(){
...

Если вы сделаете это так, вы получите правильные trackID для инструкций вашего слоя

(lldb) p aArrLayerInstructions[0].trackID
(CMPersistentTrackID) $R2 = 1
(lldb) p aArrLayerInstructions[1].trackID
(CMPersistentTrackID) $R4 = 3
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...